##// END OF EJS Templates
Copy across PySide2 support from Qtconsole
Thomas Kluyver -
Show More
@@ -1,94 +1,95 b''
1 """ Import Qt in a manner suitable for an IPython kernel.
1 """ Import Qt in a manner suitable for an IPython kernel.
2
2
3 This is the import used for the `gui=qt` or `matplotlib=qt` initialization.
3 This is the import used for the `gui=qt` or `matplotlib=qt` initialization.
4
4
5 Import Priority:
5 Import Priority:
6
6
7 if Qt has been imported anywhere else:
7 if Qt has been imported anywhere else:
8 use that
8 use that
9
9
10 if matplotlib has been imported and doesn't support v2 (<= 1.0.1):
10 if matplotlib has been imported and doesn't support v2 (<= 1.0.1):
11 use PyQt4 @v1
11 use PyQt4 @v1
12
12
13 Next, ask QT_API env variable
13 Next, ask QT_API env variable
14
14
15 if QT_API not set:
15 if QT_API not set:
16 ask matplotlib what it's using. If Qt4Agg or Qt5Agg, then use the
16 ask matplotlib what it's using. If Qt4Agg or Qt5Agg, then use the
17 version matplotlib is configured with
17 version matplotlib is configured with
18
18
19 else: (matplotlib said nothing)
19 else: (matplotlib said nothing)
20 # this is the default path - nobody told us anything
20 # this is the default path - nobody told us anything
21 try in this order:
21 try in this order:
22 PyQt default version, PySide, PyQt5
22 PyQt default version, PySide, PyQt5
23 else:
23 else:
24 use what QT_API says
24 use what QT_API says
25
25
26 """
26 """
27 # NOTE: This is no longer an external, third-party module, and should be
27 # NOTE: This is no longer an external, third-party module, and should be
28 # considered part of IPython. For compatibility however, it is being kept in
28 # considered part of IPython. For compatibility however, it is being kept in
29 # IPython/external.
29 # IPython/external.
30
30
31 import os
31 import os
32 import sys
32 import sys
33
33
34 from IPython.utils.version import check_version
34 from IPython.utils.version import check_version
35 from IPython.external.qt_loaders import (load_qt, loaded_api, QT_API_PYSIDE,
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 QT_API_PYQTv1, QT_API_PYQT_DEFAULT)
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 QT_API_PYQT_DEFAULT)
40 QT_API_PYQT_DEFAULT)
41
41
42 #Constraints placed on an imported matplotlib
42 #Constraints placed on an imported matplotlib
43 def matplotlib_options(mpl):
43 def matplotlib_options(mpl):
44 if mpl is None:
44 if mpl is None:
45 return
45 return
46 backend = mpl.rcParams.get('backend', None)
46 backend = mpl.rcParams.get('backend', None)
47 if backend == 'Qt4Agg':
47 if backend == 'Qt4Agg':
48 mpqt = mpl.rcParams.get('backend.qt4', None)
48 mpqt = mpl.rcParams.get('backend.qt4', None)
49 if mpqt is None:
49 if mpqt is None:
50 return None
50 return None
51 if mpqt.lower() == 'pyside':
51 if mpqt.lower() == 'pyside':
52 return [QT_API_PYSIDE]
52 return [QT_API_PYSIDE]
53 elif mpqt.lower() == 'pyqt4':
53 elif mpqt.lower() == 'pyqt4':
54 return [QT_API_PYQT_DEFAULT]
54 return [QT_API_PYQT_DEFAULT]
55 elif mpqt.lower() == 'pyqt4v2':
55 elif mpqt.lower() == 'pyqt4v2':
56 return [QT_API_PYQT]
56 return [QT_API_PYQT]
57 raise ImportError("unhandled value for backend.qt4 from matplotlib: %r" %
57 raise ImportError("unhandled value for backend.qt4 from matplotlib: %r" %
58 mpqt)
58 mpqt)
59 elif backend == 'Qt5Agg':
59 elif backend == 'Qt5Agg':
60 mpqt = mpl.rcParams.get('backend.qt5', None)
60 mpqt = mpl.rcParams.get('backend.qt5', None)
61 if mpqt is None:
61 if mpqt is None:
62 return None
62 return None
63 if mpqt.lower() == 'pyqt5':
63 if mpqt.lower() == 'pyqt5':
64 return [QT_API_PYQT5]
64 return [QT_API_PYQT5]
65 raise ImportError("unhandled value for backend.qt5 from matplotlib: %r" %
65 raise ImportError("unhandled value for backend.qt5 from matplotlib: %r" %
66 mpqt)
66 mpqt)
67
67
68 def get_options():
68 def get_options():
69 """Return a list of acceptable QT APIs, in decreasing order of
69 """Return a list of acceptable QT APIs, in decreasing order of
70 preference
70 preference
71 """
71 """
72 #already imported Qt somewhere. Use that
72 #already imported Qt somewhere. Use that
73 loaded = loaded_api()
73 loaded = loaded_api()
74 if loaded is not None:
74 if loaded is not None:
75 return [loaded]
75 return [loaded]
76
76
77 mpl = sys.modules.get('matplotlib', None)
77 mpl = sys.modules.get('matplotlib', None)
78
78
79 if mpl is not None and not check_version(mpl.__version__, '1.0.2'):
79 if mpl is not None and not check_version(mpl.__version__, '1.0.2'):
80 #1.0.1 only supports PyQt4 v1
80 #1.0.1 only supports PyQt4 v1
81 return [QT_API_PYQT_DEFAULT]
81 return [QT_API_PYQT_DEFAULT]
82
82
83 qt_api = os.environ.get('QT_API', None)
83 qt_api = os.environ.get('QT_API', None)
84 if qt_api is None:
84 if qt_api is None:
85 #no ETS variable. Ask mpl, then use default fallback path
85 #no ETS variable. Ask mpl, then use default fallback path
86 return matplotlib_options(mpl) or [QT_API_PYQT_DEFAULT, QT_API_PYSIDE, QT_API_PYQT5]
86 return matplotlib_options(mpl) or [QT_API_PYQT_DEFAULT, QT_API_PYSIDE,
87 QT_API_PYQT5, QT_API_PYSIDE2]
87 elif qt_api not in _qt_apis:
88 elif qt_api not in _qt_apis:
88 raise RuntimeError("Invalid Qt API %r, valid values are: %r" %
89 raise RuntimeError("Invalid Qt API %r, valid values are: %r" %
89 (qt_api, ', '.join(_qt_apis)))
90 (qt_api, ', '.join(_qt_apis)))
90 else:
91 else:
91 return [qt_api]
92 return [qt_api]
92
93
93 api_opts = get_options()
94 api_opts = get_options()
94 QtCore, QtGui, QtSvg, QT_API = load_qt(api_opts)
95 QtCore, QtGui, QtSvg, QT_API = load_qt(api_opts)
@@ -1,344 +1,373 b''
1 """
1 """
2 This module contains factory functions that attempt
2 This module contains factory functions that attempt
3 to return Qt submodules from the various python Qt bindings.
3 to return Qt submodules from the various python Qt bindings.
4
4
5 It also protects against double-importing Qt with different
5 It also protects against double-importing Qt with different
6 bindings, which is unstable and likely to crash
6 bindings, which is unstable and likely to crash
7
7
8 This is used primarily by qt and qt_for_kernel, and shouldn't
8 This is used primarily by qt and qt_for_kernel, and shouldn't
9 be accessed directly from the outside
9 be accessed directly from the outside
10 """
10 """
11 import sys
11 import sys
12 import types
12 import types
13 from functools import partial
13 from functools import partial
14 from importlib import import_module
14 from importlib import import_module
15
15
16 from IPython.utils.version import check_version
16 from IPython.utils.version import check_version
17
17
18 # Available APIs.
18 # Available APIs.
19 QT_API_PYQT = 'pyqt' # Force version 2
19 QT_API_PYQT = 'pyqt' # Force version 2
20 QT_API_PYQT5 = 'pyqt5'
20 QT_API_PYQT5 = 'pyqt5'
21 QT_API_PYQTv1 = 'pyqtv1' # Force version 2
21 QT_API_PYQTv1 = 'pyqtv1' # Force version 2
22 QT_API_PYQT_DEFAULT = 'pyqtdefault' # use system default for version 1 vs. 2
22 QT_API_PYQT_DEFAULT = 'pyqtdefault' # use system default for version 1 vs. 2
23 QT_API_PYSIDE = 'pyside'
23 QT_API_PYSIDE = 'pyside'
24 QT_API_PYSIDE2 = 'pyside2'
24 QT_API_PYSIDE2 = 'pyside2'
25
25
26 api_to_module = {QT_API_PYSIDE2: 'PySide2',
26 api_to_module = {QT_API_PYSIDE2: 'PySide2',
27 QT_API_PYSIDE: 'PySide',
27 QT_API_PYSIDE: 'PySide',
28 QT_API_PYQT: 'PyQt4',
28 QT_API_PYQT: 'PyQt4',
29 QT_API_PYQTv1: 'PyQt4',
29 QT_API_PYQTv1: 'PyQt4',
30 QT_API_PYQT5: 'PyQt5',
30 QT_API_PYQT5: 'PyQt5',
31 QT_API_PYQT_DEFAULT: 'PyQt4',
31 QT_API_PYQT_DEFAULT: 'PyQt4',
32 }
32 }
33
33
34
34
35 class ImportDenier(object):
35 class ImportDenier(object):
36 """Import Hook that will guard against bad Qt imports
36 """Import Hook that will guard against bad Qt imports
37 once IPython commits to a specific binding
37 once IPython commits to a specific binding
38 """
38 """
39
39
40 def __init__(self):
40 def __init__(self):
41 self.__forbidden = set()
41 self.__forbidden = set()
42
42
43 def forbid(self, module_name):
43 def forbid(self, module_name):
44 sys.modules.pop(module_name, None)
44 sys.modules.pop(module_name, None)
45 self.__forbidden.add(module_name)
45 self.__forbidden.add(module_name)
46
46
47 def find_module(self, fullname, path=None):
47 def find_module(self, fullname, path=None):
48 if path:
48 if path:
49 return
49 return
50 if fullname in self.__forbidden:
50 if fullname in self.__forbidden:
51 return self
51 return self
52
52
53 def load_module(self, fullname):
53 def load_module(self, fullname):
54 raise ImportError("""
54 raise ImportError("""
55 Importing %s disabled by IPython, which has
55 Importing %s disabled by IPython, which has
56 already imported an Incompatible QT Binding: %s
56 already imported an Incompatible QT Binding: %s
57 """ % (fullname, loaded_api()))
57 """ % (fullname, loaded_api()))
58
58
59 ID = ImportDenier()
59 ID = ImportDenier()
60 sys.meta_path.append(ID)
60 sys.meta_path.append(ID)
61
61
62
62
63 def commit_api(api):
63 def commit_api(api):
64 """Commit to a particular API, and trigger ImportErrors on subsequent
64 """Commit to a particular API, and trigger ImportErrors on subsequent
65 dangerous imports"""
65 dangerous imports"""
66
66
67 if api == QT_API_PYSIDE2:
68 ID.forbid('PySide')
69 ID.forbid('PyQt4')
70 ID.forbid('PyQt5')
67 if api == QT_API_PYSIDE:
71 if api == QT_API_PYSIDE:
72 ID.forbid('PySide2')
68 ID.forbid('PyQt4')
73 ID.forbid('PyQt4')
69 ID.forbid('PyQt5')
74 ID.forbid('PyQt5')
70 elif api == QT_API_PYQT5:
75 elif api == QT_API_PYQT5:
76 ID.forbid('PySide2')
71 ID.forbid('PySide')
77 ID.forbid('PySide')
72 ID.forbid('PyQt4')
78 ID.forbid('PyQt4')
73 else: # There are three other possibilities, all representing PyQt4
79 else: # There are three other possibilities, all representing PyQt4
74 ID.forbid('PyQt5')
80 ID.forbid('PyQt5')
81 ID.forbid('PySide2')
75 ID.forbid('PySide')
82 ID.forbid('PySide')
76
83
77
84
78 def loaded_api():
85 def loaded_api():
79 """Return which API is loaded, if any
86 """Return which API is loaded, if any
80
87
81 If this returns anything besides None,
88 If this returns anything besides None,
82 importing any other Qt binding is unsafe.
89 importing any other Qt binding is unsafe.
83
90
84 Returns
91 Returns
85 -------
92 -------
86 None, 'pyside', 'pyqt', 'pyqt5', or 'pyqtv1'
93 None, 'pyside2', 'pyside', 'pyqt', 'pyqt5', or 'pyqtv1'
87 """
94 """
88 if 'PyQt4.QtCore' in sys.modules:
95 if 'PyQt4.QtCore' in sys.modules:
89 if qtapi_version() == 2:
96 if qtapi_version() == 2:
90 return QT_API_PYQT
97 return QT_API_PYQT
91 else:
98 else:
92 return QT_API_PYQTv1
99 return QT_API_PYQTv1
93 elif 'PySide.QtCore' in sys.modules:
100 elif 'PySide.QtCore' in sys.modules:
94 return QT_API_PYSIDE
101 return QT_API_PYSIDE
102 elif 'PySide2.QtCore' in sys.modules:
103 return QT_API_PYSIDE2
95 elif 'PyQt5.QtCore' in sys.modules:
104 elif 'PyQt5.QtCore' in sys.modules:
96 return QT_API_PYQT5
105 return QT_API_PYQT5
97 return None
106 return None
98
107
99
108
100 def has_binding(api):
109 def has_binding(api):
101 """Safely check for PyQt4/5 or PySide, without importing submodules
110 """Safely check for PyQt4/5, PySide or PySide2, without importing submodules
102
111
103 Supports Python <= 3.3
112 Supports Python <= 3.3
104
113
105 Parameters
114 Parameters
106 ----------
115 ----------
107 api : str [ 'pyqtv1' | 'pyqt' | 'pyqt5' | 'pyside' | 'pyqtdefault']
116 api : str [ 'pyqtv1' | 'pyqt' | 'pyqt5' | 'pyside' | 'pyside2' | 'pyqtdefault']
108 Which module to check for
117 Which module to check for
109
118
110 Returns
119 Returns
111 -------
120 -------
112 True if the relevant module appears to be importable
121 True if the relevant module appears to be importable
113 """
122 """
114 # we can't import an incomplete pyside and pyqt4
123 # we can't import an incomplete pyside and pyqt4
115 # this will cause a crash in sip (#1431)
124 # this will cause a crash in sip (#1431)
116 # check for complete presence before importing
125 # check for complete presence before importing
117 module_name = api_to_module[api]
126 module_name = api_to_module[api]
118
127
119 import imp
128 import imp
120 try:
129 try:
121 #importing top level PyQt4/PySide module is ok...
130 #importing top level PyQt4/PySide module is ok...
122 mod = import_module(module_name)
131 mod = import_module(module_name)
123 #...importing submodules is not
132 #...importing submodules is not
124 imp.find_module('QtCore', mod.__path__)
133 imp.find_module('QtCore', mod.__path__)
125 imp.find_module('QtGui', mod.__path__)
134 imp.find_module('QtGui', mod.__path__)
126 imp.find_module('QtSvg', mod.__path__)
135 imp.find_module('QtSvg', mod.__path__)
127 if api == QT_API_PYQT5:
136 if api in (QT_API_PYQT5, QT_API_PYSIDE2):
128 # QT5 requires QtWidgets too
137 # QT5 requires QtWidgets too
129 imp.find_module('QtWidgets', mod.__path__)
138 imp.find_module('QtWidgets', mod.__path__)
130
139
131 #we can also safely check PySide version
140 #we can also safely check PySide version
132 if api == QT_API_PYSIDE:
141 if api == QT_API_PYSIDE:
133 return check_version(mod.__version__, '1.0.3')
142 return check_version(mod.__version__, '1.0.3')
134 else:
143 else:
135 return True
144 return True
136 except ImportError:
145 except ImportError:
137 return False
146 return False
138
147
139 def has_binding_new(api):
148 def has_binding_new(api):
140 """Safely check for PyQt4/5 or PySide, without importing submodules
149 """Safely check for PyQt4/5, PySide or PySide2, without importing submodules
141
150
142 Supports Python >= 3.4
151 Supports Python >= 3.4
143
152
144 Parameters
153 Parameters
145 ----------
154 ----------
146 api : str [ 'pyqtv1' | 'pyqt' | 'pyqt5' | 'pyside' | 'pyqtdefault']
155 api : str [ 'pyqtv1' | 'pyqt' | 'pyqt5' | 'pyside' | 'pyside2' | 'pyqtdefault']
147 Which module to check for
156 Which module to check for
148
157
149 Returns
158 Returns
150 -------
159 -------
151 True if the relevant module appears to be importable
160 True if the relevant module appears to be importable
152 """
161 """
153 module_name = api_to_module[api]
162 module_name = api_to_module[api]
154 from importlib.util import find_spec
163 from importlib.util import find_spec
155
164
156 required = ['QtCore', 'QtGui', 'QtSvg']
165 required = ['QtCore', 'QtGui', 'QtSvg']
157 if api in (QT_API_PYQT5, QT_API_PYSIDE2):
166 if api in (QT_API_PYQT5, QT_API_PYSIDE2):
158 # QT5 requires QtWidgets too
167 # QT5 requires QtWidgets too
159 required.append('QtWidgets')
168 required.append('QtWidgets')
160
169
161 for submod in required:
170 for submod in required:
162 try:
171 try:
163 spec = find_spec('%s.%s' % (module_name, submod))
172 spec = find_spec('%s.%s' % (module_name, submod))
164 except ImportError:
173 except ImportError:
165 # Package (e.g. PyQt5) not found
174 # Package (e.g. PyQt5) not found
166 return False
175 return False
167 else:
176 else:
168 if spec is None:
177 if spec is None:
169 # Submodule (e.g. PyQt5.QtCore) not found
178 # Submodule (e.g. PyQt5.QtCore) not found
170 return False
179 return False
171
180
172 if api == QT_API_PYSIDE:
181 if api == QT_API_PYSIDE:
173 # We can also safely check PySide version
182 # We can also safely check PySide version
174 import PySide
183 import PySide
175 return check_version(PySide.__version__, '1.0.3')
184 return check_version(PySide.__version__, '1.0.3')
176
185
177 return True
186 return True
178
187
179 if sys.version_info >= (3, 4):
188 if sys.version_info >= (3, 4):
180 has_binding = has_binding_new
189 has_binding = has_binding_new
181
190
182 def qtapi_version():
191 def qtapi_version():
183 """Return which QString API has been set, if any
192 """Return which QString API has been set, if any
184
193
185 Returns
194 Returns
186 -------
195 -------
187 The QString API version (1 or 2), or None if not set
196 The QString API version (1 or 2), or None if not set
188 """
197 """
189 try:
198 try:
190 import sip
199 import sip
191 except ImportError:
200 except ImportError:
192 return
201 return
193 try:
202 try:
194 return sip.getapi('QString')
203 return sip.getapi('QString')
195 except ValueError:
204 except ValueError:
196 return
205 return
197
206
198
207
199 def can_import(api):
208 def can_import(api):
200 """Safely query whether an API is importable, without importing it"""
209 """Safely query whether an API is importable, without importing it"""
201 if not has_binding(api):
210 if not has_binding(api):
202 return False
211 return False
203
212
204 current = loaded_api()
213 current = loaded_api()
205 if api == QT_API_PYQT_DEFAULT:
214 if api == QT_API_PYQT_DEFAULT:
206 return current in [QT_API_PYQT, QT_API_PYQTv1, None]
215 return current in [QT_API_PYQT, QT_API_PYQTv1, None]
207 else:
216 else:
208 return current in [api, None]
217 return current in [api, None]
209
218
210
219
211 def import_pyqt4(version=2):
220 def import_pyqt4(version=2):
212 """
221 """
213 Import PyQt4
222 Import PyQt4
214
223
215 Parameters
224 Parameters
216 ----------
225 ----------
217 version : 1, 2, or None
226 version : 1, 2, or None
218 Which QString/QVariant API to use. Set to None to use the system
227 Which QString/QVariant API to use. Set to None to use the system
219 default
228 default
220
229
221 ImportErrors rasied within this function are non-recoverable
230 ImportErrors rasied within this function are non-recoverable
222 """
231 """
223 # The new-style string API (version=2) automatically
232 # The new-style string API (version=2) automatically
224 # converts QStrings to Unicode Python strings. Also, automatically unpacks
233 # converts QStrings to Unicode Python strings. Also, automatically unpacks
225 # QVariants to their underlying objects.
234 # QVariants to their underlying objects.
226 import sip
235 import sip
227
236
228 if version is not None:
237 if version is not None:
229 sip.setapi('QString', version)
238 sip.setapi('QString', version)
230 sip.setapi('QVariant', version)
239 sip.setapi('QVariant', version)
231
240
232 from PyQt4 import QtGui, QtCore, QtSvg
241 from PyQt4 import QtGui, QtCore, QtSvg
233
242
234 if not check_version(QtCore.PYQT_VERSION_STR, '4.7'):
243 if not check_version(QtCore.PYQT_VERSION_STR, '4.7'):
235 raise ImportError("IPython requires PyQt4 >= 4.7, found %s" %
244 raise ImportError("IPython requires PyQt4 >= 4.7, found %s" %
236 QtCore.PYQT_VERSION_STR)
245 QtCore.PYQT_VERSION_STR)
237
246
238 # Alias PyQt-specific functions for PySide compatibility.
247 # Alias PyQt-specific functions for PySide compatibility.
239 QtCore.Signal = QtCore.pyqtSignal
248 QtCore.Signal = QtCore.pyqtSignal
240 QtCore.Slot = QtCore.pyqtSlot
249 QtCore.Slot = QtCore.pyqtSlot
241
250
242 # query for the API version (in case version == None)
251 # query for the API version (in case version == None)
243 version = sip.getapi('QString')
252 version = sip.getapi('QString')
244 api = QT_API_PYQTv1 if version == 1 else QT_API_PYQT
253 api = QT_API_PYQTv1 if version == 1 else QT_API_PYQT
245 return QtCore, QtGui, QtSvg, api
254 return QtCore, QtGui, QtSvg, api
246
255
247
256
248 def import_pyqt5():
257 def import_pyqt5():
249 """
258 """
250 Import PyQt5
259 Import PyQt5
251
260
252 ImportErrors rasied within this function are non-recoverable
261 ImportErrors rasied within this function are non-recoverable
253 """
262 """
254 import sip
263 import sip
255
264
256 from PyQt5 import QtCore, QtSvg, QtWidgets, QtGui
265 from PyQt5 import QtCore, QtSvg, QtWidgets, QtGui
257
266
258 # Alias PyQt-specific functions for PySide compatibility.
267 # Alias PyQt-specific functions for PySide compatibility.
259 QtCore.Signal = QtCore.pyqtSignal
268 QtCore.Signal = QtCore.pyqtSignal
260 QtCore.Slot = QtCore.pyqtSlot
269 QtCore.Slot = QtCore.pyqtSlot
261
270
262 # Join QtGui and QtWidgets for Qt4 compatibility.
271 # Join QtGui and QtWidgets for Qt4 compatibility.
263 QtGuiCompat = types.ModuleType('QtGuiCompat')
272 QtGuiCompat = types.ModuleType('QtGuiCompat')
264 QtGuiCompat.__dict__.update(QtGui.__dict__)
273 QtGuiCompat.__dict__.update(QtGui.__dict__)
265 QtGuiCompat.__dict__.update(QtWidgets.__dict__)
274 QtGuiCompat.__dict__.update(QtWidgets.__dict__)
266
275
267 api = QT_API_PYQT5
276 api = QT_API_PYQT5
268 return QtCore, QtGuiCompat, QtSvg, api
277 return QtCore, QtGuiCompat, QtSvg, api
269
278
270
279
271 def import_pyside():
280 def import_pyside():
272 """
281 """
273 Import PySide
282 Import PySide
274
283
275 ImportErrors raised within this function are non-recoverable
284 ImportErrors raised within this function are non-recoverable
276 """
285 """
277 from PySide import QtGui, QtCore, QtSvg
286 from PySide import QtGui, QtCore, QtSvg
278 return QtCore, QtGui, QtSvg, QT_API_PYSIDE
287 return QtCore, QtGui, QtSvg, QT_API_PYSIDE
279
288
289 def import_pyside2():
290 """
291 Import PySide2
292
293 ImportErrors raised within this function are non-recoverable
294 """
295 from PySide2 import QtGui, QtCore, QtSvg, QtWidgets, QtPrintSupport
296
297 # Join QtGui and QtWidgets for Qt4 compatibility.
298 QtGuiCompat = types.ModuleType('QtGuiCompat')
299 QtGuiCompat.__dict__.update(QtGui.__dict__)
300 QtGuiCompat.__dict__.update(QtWidgets.__dict__)
301 QtGuiCompat.__dict__.update(QtPrintSupport.__dict__)
302
303 return QtCore, QtGuiCompat, QtSvg, QT_API_PYSIDE2
304
280
305
281 def load_qt(api_options):
306 def load_qt(api_options):
282 """
307 """
283 Attempt to import Qt, given a preference list
308 Attempt to import Qt, given a preference list
284 of permissible bindings
309 of permissible bindings
285
310
286 It is safe to call this function multiple times.
311 It is safe to call this function multiple times.
287
312
288 Parameters
313 Parameters
289 ----------
314 ----------
290 api_options: List of strings
315 api_options: List of strings
291 The order of APIs to try. Valid items are 'pyside',
316 The order of APIs to try. Valid items are 'pyside', 'pyside2',
292 'pyqt', 'pyqt5', 'pyqtv1' and 'pyqtdefault'
317 'pyqt', 'pyqt5', 'pyqtv1' and 'pyqtdefault'
293
318
294 Returns
319 Returns
295 -------
320 -------
296
321
297 A tuple of QtCore, QtGui, QtSvg, QT_API
322 A tuple of QtCore, QtGui, QtSvg, QT_API
298 The first three are the Qt modules. The last is the
323 The first three are the Qt modules. The last is the
299 string indicating which module was loaded.
324 string indicating which module was loaded.
300
325
301 Raises
326 Raises
302 ------
327 ------
303 ImportError, if it isn't possible to import any requested
328 ImportError, if it isn't possible to import any requested
304 bindings (either becaues they aren't installed, or because
329 bindings (either becaues they aren't installed, or because
305 an incompatible library has already been installed)
330 an incompatible library has already been installed)
306 """
331 """
307 loaders = {QT_API_PYSIDE: import_pyside,
332 loaders = {
333 QT_API_PYSIDE2: import_pyside2,
334 QT_API_PYSIDE: import_pyside,
308 QT_API_PYQT: import_pyqt4,
335 QT_API_PYQT: import_pyqt4,
309 QT_API_PYQT5: import_pyqt5,
336 QT_API_PYQT5: import_pyqt5,
310 QT_API_PYQTv1: partial(import_pyqt4, version=1),
337 QT_API_PYQTv1: partial(import_pyqt4, version=1),
311 QT_API_PYQT_DEFAULT: partial(import_pyqt4, version=None)
338 QT_API_PYQT_DEFAULT: partial(import_pyqt4, version=None)
312 }
339 }
313
340
314 for api in api_options:
341 for api in api_options:
315
342
316 if api not in loaders:
343 if api not in loaders:
317 raise RuntimeError(
344 raise RuntimeError(
318 "Invalid Qt API %r, valid values are: %s" %
345 "Invalid Qt API %r, valid values are: %s" %
319 (api, ", ".join(["%r" % k for k in loaders.keys()])))
346 (api, ", ".join(["%r" % k for k in loaders.keys()])))
320
347
321 if not can_import(api):
348 if not can_import(api):
322 continue
349 continue
323
350
324 #cannot safely recover from an ImportError during this
351 #cannot safely recover from an ImportError during this
325 result = loaders[api]()
352 result = loaders[api]()
326 api = result[-1] # changed if api = QT_API_PYQT_DEFAULT
353 api = result[-1] # changed if api = QT_API_PYQT_DEFAULT
327 commit_api(api)
354 commit_api(api)
328 return result
355 return result
329 else:
356 else:
330 raise ImportError("""
357 raise ImportError("""
331 Could not load requested Qt binding. Please ensure that
358 Could not load requested Qt binding. Please ensure that
332 PyQt4 >= 4.7, PyQt5 or PySide >= 1.0.3 is available,
359 PyQt4 >= 4.7, PyQt5, PySide >= 1.0.3 or PySide2 is available,
333 and only one is imported per session.
360 and only one is imported per session.
334
361
335 Currently-imported Qt library: %r
362 Currently-imported Qt library: %r
336 PyQt4 available (requires QtCore, QtGui, QtSvg): %s
363 PyQt4 available (requires QtCore, QtGui, QtSvg): %s
337 PyQt5 available (requires QtCore, QtGui, QtSvg, QtWidgets): %s
364 PyQt5 available (requires QtCore, QtGui, QtSvg, QtWidgets): %s
338 PySide >= 1.0.3 installed: %s
365 PySide >= 1.0.3 installed: %s
366 PySide2 installed: %s
339 Tried to load: %r
367 Tried to load: %r
340 """ % (loaded_api(),
368 """ % (loaded_api(),
341 has_binding(QT_API_PYQT),
369 has_binding(QT_API_PYQT),
342 has_binding(QT_API_PYQT5),
370 has_binding(QT_API_PYQT5),
343 has_binding(QT_API_PYSIDE),
371 has_binding(QT_API_PYSIDE),
372 has_binding(QT_API_PYSIDE2),
344 api_options))
373 api_options))
General Comments 0
You need to be logged in to leave comments. Login now