##// END OF EJS Templates
Fix typo
Emilio Graff -
Show More
@@ -1,408 +1,408 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 importlib.abc
11 import importlib.abc
12 import sys
12 import sys
13 import types
13 import types
14 from functools import partial, lru_cache
14 from functools import partial, lru_cache
15 import operator
15 import operator
16
16
17 # ### Available APIs.
17 # ### Available APIs.
18 # Qt6
18 # Qt6
19 QT_API_PYQT6 = "pyqt6"
19 QT_API_PYQT6 = "pyqt6"
20 QT_API_PYSIDE6 = "pyside6"
20 QT_API_PYSIDE6 = "pyside6"
21
21
22 # Qt5
22 # Qt5
23 QT_API_PYQT5 = 'pyqt5'
23 QT_API_PYQT5 = 'pyqt5'
24 QT_API_PYSIDE2 = 'pyside2'
24 QT_API_PYSIDE2 = 'pyside2'
25
25
26 # Qt4
26 # Qt4
27 QT_API_PYQT = "pyqt" # Force version 2
27 QT_API_PYQT = "pyqt" # Force version 2
28 QT_API_PYQTv1 = "pyqtv1" # Force version 2
28 QT_API_PYQTv1 = "pyqtv1" # Force version 2
29 QT_API_PYSIDE = "pyside"
29 QT_API_PYSIDE = "pyside"
30
30
31 QT_API_PYQT_DEFAULT = "pyqtdefault" # use system default for version 1 vs. 2
31 QT_API_PYQT_DEFAULT = "pyqtdefault" # use system default for version 1 vs. 2
32
32
33 api_to_module = {
33 api_to_module = {
34 # Qt6
34 # Qt6
35 QT_API_PYQT6: "PyQt6",
35 QT_API_PYQT6: "PyQt6",
36 QT_API_PYSIDE6: "PySide6",
36 QT_API_PYSIDE6: "PySide6",
37 # Qt5
37 # Qt5
38 QT_API_PYQT5: "PyQt5",
38 QT_API_PYQT5: "PyQt5",
39 QT_API_PYSIDE2: "PySide2",
39 QT_API_PYSIDE2: "PySide2",
40 # Qt4
40 # Qt4
41 QT_API_PYSIDE: "PySide",
41 QT_API_PYSIDE: "PySide",
42 QT_API_PYQT: "PyQt4",
42 QT_API_PYQT: "PyQt4",
43 QT_API_PYQTv1: "PyQt4",
43 QT_API_PYQTv1: "PyQt4",
44 # default
44 # default
45 QT_API_PYQT_DEFAULT: "PyQt6",
45 QT_API_PYQT_DEFAULT: "PyQt6",
46 }
46 }
47
47
48
48
49 class ImportDenier(importlib.abc.MetaPathFinder):
49 class ImportDenier(importlib.abc.MetaPathFinder):
50 """Import Hook that will guard against bad Qt imports
50 """Import Hook that will guard against bad Qt imports
51 once IPython commits to a specific binding
51 once IPython commits to a specific binding
52 """
52 """
53
53
54 def __init__(self):
54 def __init__(self):
55 self.__forbidden = set()
55 self.__forbidden = set()
56
56
57 def forbid(self, module_name):
57 def forbid(self, module_name):
58 sys.modules.pop(module_name, None)
58 sys.modules.pop(module_name, None)
59 self.__forbidden.add(module_name)
59 self.__forbidden.add(module_name)
60
60
61 def find_spec(self, fullname, path, target=None):
61 def find_spec(self, fullname, path, target=None):
62 if path:
62 if path:
63 return
63 return
64 if fullname in self.__forbidden:
64 if fullname in self.__forbidden:
65 raise ImportError(
65 raise ImportError(
66 """
66 """
67 Importing %s disabled by IPython, which has
67 Importing %s disabled by IPython, which has
68 already imported an Incompatible QT Binding: %s
68 already imported an Incompatible QT Binding: %s
69 """
69 """
70 % (fullname, loaded_api())
70 % (fullname, loaded_api())
71 )
71 )
72
72
73
73
74 ID = ImportDenier()
74 ID = ImportDenier()
75 sys.meta_path.insert(0, ID)
75 sys.meta_path.insert(0, ID)
76
76
77
77
78 def commit_api(api):
78 def commit_api(api):
79 """Commit to a particular API, and trigger ImportErrors on subsequent
79 """Commit to a particular API, and trigger ImportErrors on subsequent
80 dangerous imports"""
80 dangerous imports"""
81 modules = set(api_to_module.values())
81 modules = set(api_to_module.values())
82
82
83 modules.remove(api_to_module[api])
83 modules.remove(api_to_module[api])
84 for mod in modules:
84 for mod in modules:
85 ID.forbid(mod)
85 ID.forbid(mod)
86
86
87
87
88 def loaded_api():
88 def loaded_api():
89 """Return which API is loaded, if any
89 """Return which API is loaded, if any
90
90
91 If this returns anything besides None,
91 If this returns anything besides None,
92 importing any other Qt binding is unsafe.
92 importing any other Qt binding is unsafe.
93
93
94 Returns
94 Returns
95 -------
95 -------
96 None, 'pyside6', 'pyqt6', 'pyside2', 'pyside', 'pyqt', 'pyqt5', 'pyqtv1'
96 None, 'pyside6', 'pyqt6', 'pyside2', 'pyside', 'pyqt', 'pyqt5', 'pyqtv1'
97 """
97 """
98 if sys.modules.get("PyQt6.QtCore"):
98 if sys.modules.get("PyQt6.QtCore"):
99 return QT_API_PYQT6
99 return QT_API_PYQT6
100 elif sys.modules.get("PySide6.QtCore"):
100 elif sys.modules.get("PySide6.QtCore"):
101 return QT_API_PYSIDE6
101 return QT_API_PYSIDE6
102 elif sys.modules.get("PyQt5.QtCore"):
102 elif sys.modules.get("PyQt5.QtCore"):
103 return QT_API_PYQT5
103 return QT_API_PYQT5
104 elif sys.modules.get("PySide2.QtCore"):
104 elif sys.modules.get("PySide2.QtCore"):
105 return QT_API_PYSIDE2
105 return QT_API_PYSIDE2
106 elif sys.modules.get("PyQt4.QtCore"):
106 elif sys.modules.get("PyQt4.QtCore"):
107 if qtapi_version() == 2:
107 if qtapi_version() == 2:
108 return QT_API_PYQT
108 return QT_API_PYQT
109 else:
109 else:
110 return QT_API_PYQTv1
110 return QT_API_PYQTv1
111 elif sys.modules.get("PySide.QtCore"):
111 elif sys.modules.get("PySide.QtCore"):
112 return QT_API_PYSIDE
112 return QT_API_PYSIDE
113
113
114 return None
114 return None
115
115
116
116
117 def has_binding(api):
117 def has_binding(api):
118 """Safely check for PyQt4/5, PySide or PySide2, without importing submodules
118 """Safely check for PyQt4/5, PySide or PySide2, without importing submodules
119
119
120 Parameters
120 Parameters
121 ----------
121 ----------
122 api : str [ 'pyqtv1' | 'pyqt' | 'pyqt5' | 'pyside' | 'pyside2' | 'pyqtdefault']
122 api : str [ 'pyqtv1' | 'pyqt' | 'pyqt5' | 'pyside' | 'pyside2' | 'pyqtdefault']
123 Which module to check for
123 Which module to check for
124
124
125 Returns
125 Returns
126 -------
126 -------
127 True if the relevant module appears to be importable
127 True if the relevant module appears to be importable
128 """
128 """
129 module_name = api_to_module[api]
129 module_name = api_to_module[api]
130 from importlib.util import find_spec
130 from importlib.util import find_spec
131
131
132 required = ['QtCore', 'QtGui', 'QtSvg']
132 required = ['QtCore', 'QtGui', 'QtSvg']
133 if api in (QT_API_PYQT5, QT_API_PYSIDE2, QT_API_PYQT6, QT_API_PYSIDE6):
133 if api in (QT_API_PYQT5, QT_API_PYSIDE2, QT_API_PYQT6, QT_API_PYSIDE6):
134 # QT5 requires QtWidgets too
134 # QT5 requires QtWidgets too
135 required.append('QtWidgets')
135 required.append('QtWidgets')
136
136
137 for submod in required:
137 for submod in required:
138 try:
138 try:
139 spec = find_spec('%s.%s' % (module_name, submod))
139 spec = find_spec('%s.%s' % (module_name, submod))
140 except ImportError:
140 except ImportError:
141 # Package (e.g. PyQt5) not found
141 # Package (e.g. PyQt5) not found
142 return False
142 return False
143 else:
143 else:
144 if spec is None:
144 if spec is None:
145 # Submodule (e.g. PyQt5.QtCore) not found
145 # Submodule (e.g. PyQt5.QtCore) not found
146 return False
146 return False
147
147
148 if api == QT_API_PYSIDE:
148 if api == QT_API_PYSIDE:
149 # We can also safely check PySide version
149 # We can also safely check PySide version
150 import PySide
150 import PySide
151
151
152 return PySide.__version_info__ >= (1, 0, 3)
152 return PySide.__version_info__ >= (1, 0, 3)
153
153
154 return True
154 return True
155
155
156
156
157 def qtapi_version():
157 def qtapi_version():
158 """Return which QString API has been set, if any
158 """Return which QString API has been set, if any
159
159
160 Returns
160 Returns
161 -------
161 -------
162 The QString API version (1 or 2), or None if not set
162 The QString API version (1 or 2), or None if not set
163 """
163 """
164 try:
164 try:
165 import sip
165 import sip
166 except ImportError:
166 except ImportError:
167 # as of PyQt5 5.11, sip is no longer available as a top-level
167 # as of PyQt5 5.11, sip is no longer available as a top-level
168 # module and needs to be imported from the PyQt5 namespace
168 # module and needs to be imported from the PyQt5 namespace
169 try:
169 try:
170 from PyQt5 import sip
170 from PyQt5 import sip
171 except ImportError:
171 except ImportError:
172 return
172 return
173 try:
173 try:
174 return sip.getapi('QString')
174 return sip.getapi('QString')
175 except ValueError:
175 except ValueError:
176 return
176 return
177
177
178
178
179 def can_import(api):
179 def can_import(api):
180 """Safely query whether an API is importable, without importing it"""
180 """Safely query whether an API is importable, without importing it"""
181 if not has_binding(api):
181 if not has_binding(api):
182 return False
182 return False
183
183
184 current = loaded_api()
184 current = loaded_api()
185 if api == QT_API_PYQT_DEFAULT:
185 if api == QT_API_PYQT_DEFAULT:
186 return current in [QT_API_PYQT6, None]
186 return current in [QT_API_PYQT6, None]
187 else:
187 else:
188 return current in [api, None]
188 return current in [api, None]
189
189
190
190
191 def import_pyqt4(version=2):
191 def import_pyqt4(version=2):
192 """
192 """
193 Import PyQt4
193 Import PyQt4
194
194
195 Parameters
195 Parameters
196 ----------
196 ----------
197 version : 1, 2, or None
197 version : 1, 2, or None
198 Which QString/QVariant API to use. Set to None to use the system
198 Which QString/QVariant API to use. Set to None to use the system
199 default
199 default
200 ImportErrors raised within this function are non-recoverable
200 ImportErrors raised within this function are non-recoverable
201 """
201 """
202 # The new-style string API (version=2) automatically
202 # The new-style string API (version=2) automatically
203 # converts QStrings to Unicode Python strings. Also, automatically unpacks
203 # converts QStrings to Unicode Python strings. Also, automatically unpacks
204 # QVariants to their underlying objects.
204 # QVariants to their underlying objects.
205 import sip
205 import sip
206
206
207 if version is not None:
207 if version is not None:
208 sip.setapi('QString', version)
208 sip.setapi('QString', version)
209 sip.setapi('QVariant', version)
209 sip.setapi('QVariant', version)
210
210
211 from PyQt4 import QtGui, QtCore, QtSvg
211 from PyQt4 import QtGui, QtCore, QtSvg
212
212
213 if QtCore.PYQT_VERSION < 0x040700:
213 if QtCore.PYQT_VERSION < 0x040700:
214 raise ImportError("IPython requires PyQt4 >= 4.7, found %s" %
214 raise ImportError("IPython requires PyQt4 >= 4.7, found %s" %
215 QtCore.PYQT_VERSION_STR)
215 QtCore.PYQT_VERSION_STR)
216
216
217 # Alias PyQt-specific functions for PySide compatibility.
217 # Alias PyQt-specific functions for PySide compatibility.
218 QtCore.Signal = QtCore.pyqtSignal
218 QtCore.Signal = QtCore.pyqtSignal
219 QtCore.Slot = QtCore.pyqtSlot
219 QtCore.Slot = QtCore.pyqtSlot
220
220
221 # query for the API version (in case version == None)
221 # query for the API version (in case version == None)
222 version = sip.getapi('QString')
222 version = sip.getapi('QString')
223 api = QT_API_PYQTv1 if version == 1 else QT_API_PYQT
223 api = QT_API_PYQTv1 if version == 1 else QT_API_PYQT
224 return QtCore, QtGui, QtSvg, api
224 return QtCore, QtGui, QtSvg, api
225
225
226
226
227 def import_pyqt5():
227 def import_pyqt5():
228 """
228 """
229 Import PyQt5
229 Import PyQt5
230
230
231 ImportErrors raised within this function are non-recoverable
231 ImportErrors raised within this function are non-recoverable
232 """
232 """
233
233
234 from PyQt5 import QtCore, QtSvg, QtWidgets, QtGui
234 from PyQt5 import QtCore, QtSvg, QtWidgets, QtGui
235
235
236 # Alias PyQt-specific functions for PySide compatibility.
236 # Alias PyQt-specific functions for PySide compatibility.
237 QtCore.Signal = QtCore.pyqtSignal
237 QtCore.Signal = QtCore.pyqtSignal
238 QtCore.Slot = QtCore.pyqtSlot
238 QtCore.Slot = QtCore.pyqtSlot
239
239
240 # Join QtGui and QtWidgets for Qt4 compatibility.
240 # Join QtGui and QtWidgets for Qt4 compatibility.
241 QtGuiCompat = types.ModuleType('QtGuiCompat')
241 QtGuiCompat = types.ModuleType('QtGuiCompat')
242 QtGuiCompat.__dict__.update(QtGui.__dict__)
242 QtGuiCompat.__dict__.update(QtGui.__dict__)
243 QtGuiCompat.__dict__.update(QtWidgets.__dict__)
243 QtGuiCompat.__dict__.update(QtWidgets.__dict__)
244
244
245 api = QT_API_PYQT5
245 api = QT_API_PYQT5
246 return QtCore, QtGuiCompat, QtSvg, api
246 return QtCore, QtGuiCompat, QtSvg, api
247
247
248
248
249 def import_pyqt6():
249 def import_pyqt6():
250 """
250 """
251 Import PyQt6
251 Import PyQt6
252
252
253 ImportErrors raised within this function are non-recoverable
253 ImportErrors raised within this function are non-recoverable
254 """
254 """
255
255
256 from PyQt6 import QtCore, QtSvg, QtWidgets, QtGui
256 from PyQt6 import QtCore, QtSvg, QtWidgets, QtGui
257
257
258 # Alias PyQt-specific functions for PySide compatibility.
258 # Alias PyQt-specific functions for PySide compatibility.
259 QtCore.Signal = QtCore.pyqtSignal
259 QtCore.Signal = QtCore.pyqtSignal
260 QtCore.Slot = QtCore.pyqtSlot
260 QtCore.Slot = QtCore.pyqtSlot
261
261
262 # Join QtGui and QtWidgets for Qt4 compatibility.
262 # Join QtGui and QtWidgets for Qt4 compatibility.
263 QtGuiCompat = types.ModuleType("QtGuiCompat")
263 QtGuiCompat = types.ModuleType("QtGuiCompat")
264 QtGuiCompat.__dict__.update(QtGui.__dict__)
264 QtGuiCompat.__dict__.update(QtGui.__dict__)
265 QtGuiCompat.__dict__.update(QtWidgets.__dict__)
265 QtGuiCompat.__dict__.update(QtWidgets.__dict__)
266
266
267 api = QT_API_PYQT6
267 api = QT_API_PYQT6
268 return QtCore, QtGuiCompat, QtSvg, api
268 return QtCore, QtGuiCompat, QtSvg, api
269
269
270
270
271 def import_pyside():
271 def import_pyside():
272 """
272 """
273 Import PySide
273 Import PySide
274
274
275 ImportErrors raised within this function are non-recoverable
275 ImportErrors raised within this function are non-recoverable
276 """
276 """
277 from PySide import QtGui, QtCore, QtSvg
277 from PySide import QtGui, QtCore, QtSvg
278 return QtCore, QtGui, QtSvg, QT_API_PYSIDE
278 return QtCore, QtGui, QtSvg, QT_API_PYSIDE
279
279
280 def import_pyside2():
280 def import_pyside2():
281 """
281 """
282 Import PySide2
282 Import PySide2
283
283
284 ImportErrors raised within this function are non-recoverable
284 ImportErrors raised within this function are non-recoverable
285 """
285 """
286 from PySide2 import QtGui, QtCore, QtSvg, QtWidgets, QtPrintSupport
286 from PySide2 import QtGui, QtCore, QtSvg, QtWidgets, QtPrintSupport
287
287
288 # Join QtGui and QtWidgets for Qt4 compatibility.
288 # Join QtGui and QtWidgets for Qt4 compatibility.
289 QtGuiCompat = types.ModuleType('QtGuiCompat')
289 QtGuiCompat = types.ModuleType('QtGuiCompat')
290 QtGuiCompat.__dict__.update(QtGui.__dict__)
290 QtGuiCompat.__dict__.update(QtGui.__dict__)
291 QtGuiCompat.__dict__.update(QtWidgets.__dict__)
291 QtGuiCompat.__dict__.update(QtWidgets.__dict__)
292 QtGuiCompat.__dict__.update(QtPrintSupport.__dict__)
292 QtGuiCompat.__dict__.update(QtPrintSupport.__dict__)
293
293
294 return QtCore, QtGuiCompat, QtSvg, QT_API_PYSIDE2
294 return QtCore, QtGuiCompat, QtSvg, QT_API_PYSIDE2
295
295
296
296
297 def import_pyside6():
297 def import_pyside6():
298 """
298 """
299 Import PySide6
299 Import PySide6
300
300
301 ImportErrors raised within this function are non-recoverable
301 ImportErrors raised within this function are non-recoverable
302 """
302 """
303 from PySide6 import QtGui, QtCore, QtSvg, QtWidgets, QtPrintSupport
303 from PySide6 import QtGui, QtCore, QtSvg, QtWidgets, QtPrintSupport
304
304
305 # Join QtGui and QtWidgets for Qt4 compatibility.
305 # Join QtGui and QtWidgets for Qt4 compatibility.
306 QtGuiCompat = types.ModuleType("QtGuiCompat")
306 QtGuiCompat = types.ModuleType("QtGuiCompat")
307 QtGuiCompat.__dict__.update(QtGui.__dict__)
307 QtGuiCompat.__dict__.update(QtGui.__dict__)
308 QtGuiCompat.__dict__.update(QtWidgets.__dict__)
308 QtGuiCompat.__dict__.update(QtWidgets.__dict__)
309 QtGuiCompat.__dict__.update(QtPrintSupport.__dict__)
309 QtGuiCompat.__dict__.update(QtPrintSupport.__dict__)
310
310
311 return QtCore, QtGuiCompat, QtSvg, QT_API_PYSIDE6
311 return QtCore, QtGuiCompat, QtSvg, QT_API_PYSIDE6
312
312
313
313
314 def load_qt(api_options):
314 def load_qt(api_options):
315 """
315 """
316 Attempt to import Qt, given a preference list
316 Attempt to import Qt, given a preference list
317 of permissible bindings
317 of permissible bindings
318
318
319 It is safe to call this function multiple times.
319 It is safe to call this function multiple times.
320
320
321 Parameters
321 Parameters
322 ----------
322 ----------
323 api_options : List of strings
323 api_options : List of strings
324 The order of APIs to try. Valid items are 'pyside', 'pyside2',
324 The order of APIs to try. Valid items are 'pyside', 'pyside2',
325 'pyqt', 'pyqt5', 'pyqtv1' and 'pyqtdefault'
325 'pyqt', 'pyqt5', 'pyqtv1' and 'pyqtdefault'
326
326
327 Returns
327 Returns
328 -------
328 -------
329 A tuple of QtCore, QtGui, QtSvg, QT_API
329 A tuple of QtCore, QtGui, QtSvg, QT_API
330 The first three are the Qt modules. The last is the
330 The first three are the Qt modules. The last is the
331 string indicating which module was loaded.
331 string indicating which module was loaded.
332
332
333 Raises
333 Raises
334 ------
334 ------
335 ImportError, if it isn't possible to import any requested
335 ImportError, if it isn't possible to import any requested
336 bindings (either because they aren't installed, or because
336 bindings (either because they aren't installed, or because
337 an incompatible library has already been installed)
337 an incompatible library has already been installed)
338 """
338 """
339 loaders = {
339 loaders = {
340 # Qt6
340 # Qt6
341 QT_API_PYQT6: import_pyqt6,
341 QT_API_PYQT6: import_pyqt6,
342 QT_API_PYSIDE6: import_pyside6,
342 QT_API_PYSIDE6: import_pyside6,
343 # Qt5
343 # Qt5
344 QT_API_PYQT5: import_pyqt5,
344 QT_API_PYQT5: import_pyqt5,
345 QT_API_PYSIDE2: import_pyside2,
345 QT_API_PYSIDE2: import_pyside2,
346 # Qt4
346 # Qt4
347 QT_API_PYSIDE: import_pyside,
347 QT_API_PYSIDE: import_pyside,
348 QT_API_PYQT: import_pyqt4,
348 QT_API_PYQT: import_pyqt4,
349 QT_API_PYQTv1: partial(import_pyqt4, version=1),
349 QT_API_PYQTv1: partial(import_pyqt4, version=1),
350 # default
350 # default
351 QT_API_PYQT_DEFAULT: import_pyqt6,
351 QT_API_PYQT_DEFAULT: import_pyqt6,
352 }
352 }
353
353
354 for api in api_options:
354 for api in api_options:
355
355
356 if api not in loaders:
356 if api not in loaders:
357 raise RuntimeError(
357 raise RuntimeError(
358 "Invalid Qt API %r, valid values are: %s" %
358 "Invalid Qt API %r, valid values are: %s" %
359 (api, ", ".join(["%r" % k for k in loaders.keys()])))
359 (api, ", ".join(["%r" % k for k in loaders.keys()])))
360
360
361 if not can_import(api):
361 if not can_import(api):
362 continue
362 continue
363
363
364 #cannot safely recover from an ImportError during this
364 #cannot safely recover from an ImportError during this
365 result = loaders[api]()
365 result = loaders[api]()
366 api = result[-1] # changed if api = QT_API_PYQT_DEFAULT
366 api = result[-1] # changed if api = QT_API_PYQT_DEFAULT
367 commit_api(api)
367 commit_api(api)
368 return result
368 return result
369 else:
369 else:
370 raise ImportError(
370 raise ImportError(
371 """
371 """
372 Could not load requested Qt binding. Please ensure that
372 Could not load requested Qt binding. Please ensure that
373 PyQt4 >= 4.7, PyQt5, PyQt6, PySide >= 1.0.3, PySide2, or
373 PyQt4 >= 4.7, PyQt5, PyQt6, PySide >= 1.0.3, PySide2, or
374 PySide6is available, and only one is imported per session.
374 PySide6 is available, and only one is imported per session.
375
375
376 Currently-imported Qt library: %r
376 Currently-imported Qt library: %r
377 PyQt4 available (requires QtCore, QtGui, QtSvg): %s
377 PyQt4 available (requires QtCore, QtGui, QtSvg): %s
378 PyQt5 available (requires QtCore, QtGui, QtSvg, QtWidgets): %s
378 PyQt5 available (requires QtCore, QtGui, QtSvg, QtWidgets): %s
379 PyQt6 available (requires QtCore, QtGui, QtSvg, QtWidgets): %s
379 PyQt6 available (requires QtCore, QtGui, QtSvg, QtWidgets): %s
380 PySide >= 1.0.3 installed: %s
380 PySide >= 1.0.3 installed: %s
381 PySide2 installed: %s
381 PySide2 installed: %s
382 PySide6 installed: %s
382 PySide6 installed: %s
383 Tried to load: %r
383 Tried to load: %r
384 """
384 """
385 % (
385 % (
386 loaded_api(),
386 loaded_api(),
387 has_binding(QT_API_PYQT),
387 has_binding(QT_API_PYQT),
388 has_binding(QT_API_PYQT5),
388 has_binding(QT_API_PYQT5),
389 has_binding(QT_API_PYQT6),
389 has_binding(QT_API_PYQT6),
390 has_binding(QT_API_PYSIDE),
390 has_binding(QT_API_PYSIDE),
391 has_binding(QT_API_PYSIDE2),
391 has_binding(QT_API_PYSIDE2),
392 has_binding(QT_API_PYSIDE6),
392 has_binding(QT_API_PYSIDE6),
393 api_options,
393 api_options,
394 )
394 )
395 )
395 )
396
396
397
397
398 def enum_factory(QT_API, QtCore):
398 def enum_factory(QT_API, QtCore):
399 """Construct an enum helper to account for PyQt5 <-> PyQt6 changes."""
399 """Construct an enum helper to account for PyQt5 <-> PyQt6 changes."""
400
400
401 @lru_cache(None)
401 @lru_cache(None)
402 def _enum(name):
402 def _enum(name):
403 # foo.bar.Enum.Entry (PyQt6) <=> foo.bar.Entry (non-PyQt6).
403 # foo.bar.Enum.Entry (PyQt6) <=> foo.bar.Entry (non-PyQt6).
404 return operator.attrgetter(
404 return operator.attrgetter(
405 name if QT_API == QT_API_PYQT6 else name.rpartition(".")[0]
405 name if QT_API == QT_API_PYQT6 else name.rpartition(".")[0]
406 )(sys.modules[QtCore.__package__])
406 )(sys.modules[QtCore.__package__])
407
407
408 return _enum
408 return _enum
General Comments 0
You need to be logged in to leave comments. Login now