##// END OF EJS Templates
Add support for PyQt5.
Peter Würtz -
Show More
@@ -1,22 +1,22 b''
1 """ A Qt API selector that can be used to switch between PyQt and PySide.
1 """ A Qt API selector that can be used to switch between PyQt4/5 and PySide.
2
2
3 This uses the ETS 4.0 selection pattern of:
3 This uses the ETS 4.0 selection pattern of:
4 PySide first, PyQt with API v2. second.
4 PySide first, PyQt4 (API v2.) second, then PyQt5.
5
5
6 Do not use this if you need PyQt with the old QString/QVariant API.
6 Do not use this if you need PyQt4 with the old QString/QVariant API.
7 """
7 """
8
8
9 import os
9 import os
10
10
11 from IPython.external.qt_loaders import (load_qt, QT_API_PYSIDE,
11 from IPython.external.qt_loaders import (load_qt, QT_API_PYSIDE,
12 QT_API_PYQT)
12 QT_API_PYQT, QT_API_PYQT5)
13
13
14 QT_API = os.environ.get('QT_API', None)
14 QT_API = os.environ.get('QT_API', None)
15 if QT_API not in [QT_API_PYSIDE, QT_API_PYQT, None]:
15 if QT_API not in [QT_API_PYSIDE, QT_API_PYQT, QT_API_PYQT5, None]:
16 raise RuntimeError("Invalid Qt API %r, valid values are: %r, %r" %
16 raise RuntimeError("Invalid Qt API %r, valid values are: %r, %r, %r" %
17 (QT_API, QT_API_PYSIDE, QT_API_PYQT))
17 (QT_API, QT_API_PYSIDE, QT_API_PYQT, QT_API_PYQT5))
18 if QT_API is None:
18 if QT_API is None:
19 api_opts = [QT_API_PYSIDE, QT_API_PYQT]
19 api_opts = [QT_API_PYSIDE, QT_API_PYQT, QT_API_PYQT5]
20 else:
20 else:
21 api_opts = [QT_API]
21 api_opts = [QT_API]
22
22
@@ -9,12 +9,14 b" 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 from functools import partial
13 from functools import partial
13
14
14 from IPython.utils.version import check_version
15 from IPython.utils.version import check_version
15
16
16 # Available APIs.
17 # Available APIs.
17 QT_API_PYQT = 'pyqt'
18 QT_API_PYQT = 'pyqt'
19 QT_API_PYQT5 = 'pyqt5'
18 QT_API_PYQTv1 = 'pyqtv1'
20 QT_API_PYQTv1 = 'pyqtv1'
19 QT_API_PYQT_DEFAULT = 'pyqtdefault' # don't set SIP explicitly
21 QT_API_PYQT_DEFAULT = 'pyqtdefault' # don't set SIP explicitly
20 QT_API_PYSIDE = 'pyside'
22 QT_API_PYSIDE = 'pyside'
@@ -26,16 +28,16 b' class ImportDenier(object):'
26 """
28 """
27
29
28 def __init__(self):
30 def __init__(self):
29 self.__forbidden = None
31 self.__forbidden = set()
30
32
31 def forbid(self, module_name):
33 def forbid(self, module_name):
32 sys.modules.pop(module_name, None)
34 sys.modules.pop(module_name, None)
33 self.__forbidden = module_name
35 self.__forbidden.add(module_name)
34
36
35 def find_module(self, mod_name, pth):
37 def find_module(self, mod_name, pth):
36 if pth:
38 if pth:
37 return
39 return
38 if mod_name == self.__forbidden:
40 if mod_name in self.__forbidden:
39 return self
41 return self
40
42
41 def load_module(self, mod_name):
43 def load_module(self, mod_name):
@@ -54,7 +56,12 b' def commit_api(api):'
54
56
55 if api == QT_API_PYSIDE:
57 if api == QT_API_PYSIDE:
56 ID.forbid('PyQt4')
58 ID.forbid('PyQt4')
59 ID.forbid('PyQt5')
60 elif api == QT_API_PYQT:
61 ID.forbid('PySide')
62 ID.forbid('PyQt5')
57 else:
63 else:
64 ID.forbid('PyQt4')
58 ID.forbid('PySide')
65 ID.forbid('PySide')
59
66
60
67
@@ -75,16 +82,18 b' def loaded_api():'
75 return QT_API_PYQTv1
82 return QT_API_PYQTv1
76 elif 'PySide.QtCore' in sys.modules:
83 elif 'PySide.QtCore' in sys.modules:
77 return QT_API_PYSIDE
84 return QT_API_PYSIDE
85 elif 'PyQt5.QtCore' in sys.modules:
86 return QT_API_PYQT5
78 return None
87 return None
79
88
80
89
81 def has_binding(api):
90 def has_binding(api):
82 """Safely check for PyQt4 or PySide, without importing
91 """Safely check for PyQt4/5 or PySide, without importing
83 submodules
92 submodules
84
93
85 Parameters
94 Parameters
86 ----------
95 ----------
87 api : str [ 'pyqtv1' | 'pyqt' | 'pyside' | 'pyqtdefault']
96 api : str [ 'pyqtv1' | 'pyqt' | 'pyqt5' | 'pyside' | 'pyqtdefault']
88 Which module to check for
97 Which module to check for
89
98
90 Returns
99 Returns
@@ -97,6 +106,7 b' def has_binding(api):'
97 module_name = {QT_API_PYSIDE: 'PySide',
106 module_name = {QT_API_PYSIDE: 'PySide',
98 QT_API_PYQT: 'PyQt4',
107 QT_API_PYQT: 'PyQt4',
99 QT_API_PYQTv1: 'PyQt4',
108 QT_API_PYQTv1: 'PyQt4',
109 QT_API_PYQT5: 'PyQt5',
100 QT_API_PYQT_DEFAULT: 'PyQt4'}
110 QT_API_PYQT_DEFAULT: 'PyQt4'}
101 module_name = module_name[api]
111 module_name = module_name[api]
102
112
@@ -108,6 +118,9 b' def has_binding(api):'
108 imp.find_module('QtCore', mod.__path__)
118 imp.find_module('QtCore', mod.__path__)
109 imp.find_module('QtGui', mod.__path__)
119 imp.find_module('QtGui', mod.__path__)
110 imp.find_module('QtSvg', mod.__path__)
120 imp.find_module('QtSvg', mod.__path__)
121 if api == QT_API_PYQT5:
122 # QT5 requires QtWidgets too
123 imp.find_module('QtWidgets', mod.__path__)
111
124
112 #we can also safely check PySide version
125 #we can also safely check PySide version
113 if api == QT_API_PYSIDE:
126 if api == QT_API_PYSIDE:
@@ -184,6 +197,29 b' def import_pyqt4(version=2):'
184 return QtCore, QtGui, QtSvg, api
197 return QtCore, QtGui, QtSvg, api
185
198
186
199
200 def import_pyqt5():
201 """
202 Import PyQt5
203
204 ImportErrors rasied within this function are non-recoverable
205 """
206 import sip
207
208 from PyQt5 import QtCore, QtSvg, QtWidgets, QtGui
209
210 # Alias PyQt-specific functions for PySide compatibility.
211 QtCore.Signal = QtCore.pyqtSignal
212 QtCore.Slot = QtCore.pyqtSlot
213
214 # Join QtGui and QtWidgets for Qt4 compatibility.
215 QtGuiCompat = types.ModuleType('QtGuiCompat')
216 QtGuiCompat.__dict__.update(QtGui.__dict__)
217 QtGuiCompat.__dict__.update(QtWidgets.__dict__)
218
219 api = QT_API_PYQT5
220 return QtCore, QtGuiCompat, QtSvg, api
221
222
187 def import_pyside():
223 def import_pyside():
188 """
224 """
189 Import PySide
225 Import PySide
@@ -205,7 +241,7 b' def load_qt(api_options):'
205 ----------
241 ----------
206 api_options: List of strings
242 api_options: List of strings
207 The order of APIs to try. Valid items are 'pyside',
243 The order of APIs to try. Valid items are 'pyside',
208 'pyqt', and 'pyqtv1'
244 'pyqt', 'pyqt5' and 'pyqtv1'
209
245
210 Returns
246 Returns
211 -------
247 -------
@@ -222,6 +258,7 b' def load_qt(api_options):'
222 """
258 """
223 loaders = {QT_API_PYSIDE: import_pyside,
259 loaders = {QT_API_PYSIDE: import_pyside,
224 QT_API_PYQT: import_pyqt4,
260 QT_API_PYQT: import_pyqt4,
261 QT_API_PYQT5: import_pyqt5,
225 QT_API_PYQTv1: partial(import_pyqt4, version=1),
262 QT_API_PYQTv1: partial(import_pyqt4, version=1),
226 QT_API_PYQT_DEFAULT: partial(import_pyqt4, version=None)
263 QT_API_PYQT_DEFAULT: partial(import_pyqt4, version=None)
227 }
264 }
@@ -230,9 +267,8 b' def load_qt(api_options):'
230
267
231 if api not in loaders:
268 if api not in loaders:
232 raise RuntimeError(
269 raise RuntimeError(
233 "Invalid Qt API %r, valid values are: %r, %r, %r, %r" %
270 "Invalid Qt API %r, valid values are: %s" %
234 (api, QT_API_PYSIDE, QT_API_PYQT,
271 (api, ", ".join(["%r" % k for k in loaders.keys()])))
235 QT_API_PYQTv1, QT_API_PYQT_DEFAULT))
236
272
237 if not can_import(api):
273 if not can_import(api):
238 continue
274 continue
@@ -245,14 +281,16 b' def load_qt(api_options):'
245 else:
281 else:
246 raise ImportError("""
282 raise ImportError("""
247 Could not load requested Qt binding. Please ensure that
283 Could not load requested Qt binding. Please ensure that
248 PyQt4 >= 4.7 or PySide >= 1.0.3 is available,
284 PyQt4 >= 4.7, PyQt5 or PySide >= 1.0.3 is available,
249 and only one is imported per session.
285 and only one is imported per session.
250
286
251 Currently-imported Qt library: %r
287 Currently-imported Qt library: %r
252 PyQt4 installed: %s
288 PyQt4 installed: %s
289 PyQt5 installed: %s
253 PySide >= 1.0.3 installed: %s
290 PySide >= 1.0.3 installed: %s
254 Tried to load: %r
291 Tried to load: %r
255 """ % (loaded_api(),
292 """ % (loaded_api(),
256 has_binding(QT_API_PYQT),
293 has_binding(QT_API_PYQT),
294 has_binding(QT_API_PYQT5),
257 has_binding(QT_API_PYSIDE),
295 has_binding(QT_API_PYSIDE),
258 api_options))
296 api_options))
@@ -28,7 +28,7 b' class InProcessChannel(object):'
28 """Base class for in-process channels."""
28 """Base class for in-process channels."""
29 proxy_methods = []
29 proxy_methods = []
30
30
31 def __init__(self, client):
31 def __init__(self, client=None):
32 super(InProcessChannel, self).__init__()
32 super(InProcessChannel, self).__init__()
33 self.client = client
33 self.client = client
34 self._is_alive = False
34 self._is_alive = False
General Comments 0
You need to be logged in to leave comments. Login now