##// 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 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 9 import os
10 10
11 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 14 QT_API = os.environ.get('QT_API', None)
15 if QT_API not in [QT_API_PYSIDE, QT_API_PYQT, None]:
16 raise RuntimeError("Invalid Qt API %r, valid values are: %r, %r" %
17 (QT_API, QT_API_PYSIDE, QT_API_PYQT))
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, %r" %
17 (QT_API, QT_API_PYSIDE, QT_API_PYQT, QT_API_PYQT5))
18 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 20 else:
21 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 9 be accessed directly from the outside
10 10 """
11 11 import sys
12 import types
12 13 from functools import partial
13 14
14 15 from IPython.utils.version import check_version
15 16
16 17 # Available APIs.
17 18 QT_API_PYQT = 'pyqt'
19 QT_API_PYQT5 = 'pyqt5'
18 20 QT_API_PYQTv1 = 'pyqtv1'
19 21 QT_API_PYQT_DEFAULT = 'pyqtdefault' # don't set SIP explicitly
20 22 QT_API_PYSIDE = 'pyside'
@@ -26,16 +28,16 b' class ImportDenier(object):'
26 28 """
27 29
28 30 def __init__(self):
29 self.__forbidden = None
31 self.__forbidden = set()
30 32
31 33 def forbid(self, module_name):
32 34 sys.modules.pop(module_name, None)
33 self.__forbidden = module_name
35 self.__forbidden.add(module_name)
34 36
35 37 def find_module(self, mod_name, pth):
36 38 if pth:
37 39 return
38 if mod_name == self.__forbidden:
40 if mod_name in self.__forbidden:
39 41 return self
40 42
41 43 def load_module(self, mod_name):
@@ -54,7 +56,12 b' def commit_api(api):'
54 56
55 57 if api == QT_API_PYSIDE:
56 58 ID.forbid('PyQt4')
59 ID.forbid('PyQt5')
60 elif api == QT_API_PYQT:
61 ID.forbid('PySide')
62 ID.forbid('PyQt5')
57 63 else:
64 ID.forbid('PyQt4')
58 65 ID.forbid('PySide')
59 66
60 67
@@ -75,16 +82,18 b' def loaded_api():'
75 82 return QT_API_PYQTv1
76 83 elif 'PySide.QtCore' in sys.modules:
77 84 return QT_API_PYSIDE
85 elif 'PyQt5.QtCore' in sys.modules:
86 return QT_API_PYQT5
78 87 return None
79 88
80 89
81 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 92 submodules
84 93
85 94 Parameters
86 95 ----------
87 api : str [ 'pyqtv1' | 'pyqt' | 'pyside' | 'pyqtdefault']
96 api : str [ 'pyqtv1' | 'pyqt' | 'pyqt5' | 'pyside' | 'pyqtdefault']
88 97 Which module to check for
89 98
90 99 Returns
@@ -97,6 +106,7 b' def has_binding(api):'
97 106 module_name = {QT_API_PYSIDE: 'PySide',
98 107 QT_API_PYQT: 'PyQt4',
99 108 QT_API_PYQTv1: 'PyQt4',
109 QT_API_PYQT5: 'PyQt5',
100 110 QT_API_PYQT_DEFAULT: 'PyQt4'}
101 111 module_name = module_name[api]
102 112
@@ -108,6 +118,9 b' def has_binding(api):'
108 118 imp.find_module('QtCore', mod.__path__)
109 119 imp.find_module('QtGui', mod.__path__)
110 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 125 #we can also safely check PySide version
113 126 if api == QT_API_PYSIDE:
@@ -184,6 +197,29 b' def import_pyqt4(version=2):'
184 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 223 def import_pyside():
188 224 """
189 225 Import PySide
@@ -205,7 +241,7 b' def load_qt(api_options):'
205 241 ----------
206 242 api_options: List of strings
207 243 The order of APIs to try. Valid items are 'pyside',
208 'pyqt', and 'pyqtv1'
244 'pyqt', 'pyqt5' and 'pyqtv1'
209 245
210 246 Returns
211 247 -------
@@ -222,6 +258,7 b' def load_qt(api_options):'
222 258 """
223 259 loaders = {QT_API_PYSIDE: import_pyside,
224 260 QT_API_PYQT: import_pyqt4,
261 QT_API_PYQT5: import_pyqt5,
225 262 QT_API_PYQTv1: partial(import_pyqt4, version=1),
226 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 268 if api not in loaders:
232 269 raise RuntimeError(
233 "Invalid Qt API %r, valid values are: %r, %r, %r, %r" %
234 (api, QT_API_PYSIDE, QT_API_PYQT,
235 QT_API_PYQTv1, QT_API_PYQT_DEFAULT))
270 "Invalid Qt API %r, valid values are: %s" %
271 (api, ", ".join(["%r" % k for k in loaders.keys()])))
236 272
237 273 if not can_import(api):
238 274 continue
@@ -245,14 +281,16 b' def load_qt(api_options):'
245 281 else:
246 282 raise ImportError("""
247 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 285 and only one is imported per session.
250 286
251 287 Currently-imported Qt library: %r
252 288 PyQt4 installed: %s
289 PyQt5 installed: %s
253 290 PySide >= 1.0.3 installed: %s
254 291 Tried to load: %r
255 292 """ % (loaded_api(),
256 293 has_binding(QT_API_PYQT),
294 has_binding(QT_API_PYQT5),
257 295 has_binding(QT_API_PYSIDE),
258 296 api_options))
@@ -28,7 +28,7 b' class InProcessChannel(object):'
28 28 """Base class for in-process channels."""
29 29 proxy_methods = []
30 30
31 def __init__(self, client):
31 def __init__(self, client=None):
32 32 super(InProcessChannel, self).__init__()
33 33 self.client = client
34 34 self._is_alive = False
General Comments 0
You need to be logged in to leave comments. Login now