Show More
@@ -19,6 +19,7 b' backends = {' | |||||
19 | "wx": "WXAgg", |
|
19 | "wx": "WXAgg", | |
20 | "qt4": "Qt4Agg", |
|
20 | "qt4": "Qt4Agg", | |
21 | "qt5": "Qt5Agg", |
|
21 | "qt5": "Qt5Agg", | |
|
22 | "qt6": "QtAgg", | |||
22 | "qt": "Qt5Agg", |
|
23 | "qt": "Qt5Agg", | |
23 | "osx": "MacOSX", |
|
24 | "osx": "MacOSX", | |
24 | "nbagg": "nbAgg", |
|
25 | "nbagg": "nbAgg", |
@@ -32,15 +32,42 b' 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 ( |
|
35 | from IPython.external.qt_loaders import ( | |
36 | QT_API_PYSIDE2, QT_API_PYQT, QT_API_PYQT5, |
|
36 | load_qt, | |
37 | QT_API_PYQTv1, QT_API_PYQT_DEFAULT) |
|
37 | loaded_api, | |
|
38 | enum_factory, | |||
|
39 | # QT6 | |||
|
40 | QT_API_PYQT6, | |||
|
41 | QT_API_PYSIDE6, | |||
|
42 | # QT5 | |||
|
43 | QT_API_PYQT5, | |||
|
44 | QT_API_PYSIDE2, | |||
|
45 | # QT4 | |||
|
46 | QT_API_PYQTv1, | |||
|
47 | QT_API_PYQT, | |||
|
48 | QT_API_PYSIDE, | |||
|
49 | # default | |||
|
50 | QT_API_PYQT_DEFAULT, | |||
|
51 | ) | |||
|
52 | ||||
|
53 | _qt_apis = ( | |||
|
54 | # QT6 | |||
|
55 | QT_API_PYQT6, | |||
|
56 | QT_API_PYSIDE6, | |||
|
57 | # QT5 | |||
|
58 | QT_API_PYQT5, | |||
|
59 | QT_API_PYSIDE2, | |||
|
60 | # QT4 | |||
|
61 | QT_API_PYQTv1, | |||
|
62 | QT_API_PYQT, | |||
|
63 | QT_API_PYSIDE, | |||
|
64 | # default | |||
|
65 | QT_API_PYQT_DEFAULT, | |||
|
66 | ) | |||
38 |
|
67 | |||
39 | _qt_apis = (QT_API_PYSIDE, QT_API_PYSIDE2, QT_API_PYQT, QT_API_PYQT5, QT_API_PYQTv1, |
|
|||
40 | QT_API_PYQT_DEFAULT) |
|
|||
41 |
|
68 | |||
42 | #Constraints placed on an imported matplotlib |
|
|||
43 | def matplotlib_options(mpl): |
|
69 | def matplotlib_options(mpl): | |
|
70 | """Constraints placed on an imported matplotlib.""" | |||
44 | if mpl is None: |
|
71 | if mpl is None: | |
45 | return |
|
72 | return | |
46 | backend = mpl.rcParams.get('backend', None) |
|
73 | backend = mpl.rcParams.get('backend', None) | |
@@ -66,9 +93,7 b' def matplotlib_options(mpl):' | |||||
66 | mpqt) |
|
93 | mpqt) | |
67 |
|
94 | |||
68 | def get_options(): |
|
95 | def get_options(): | |
69 | """Return a list of acceptable QT APIs, in decreasing order of |
|
96 | """Return a list of acceptable QT APIs, in decreasing order of preference.""" | |
70 | preference |
|
|||
71 | """ |
|
|||
72 | #already imported Qt somewhere. Use that |
|
97 | #already imported Qt somewhere. Use that | |
73 | loaded = loaded_api() |
|
98 | loaded = loaded_api() | |
74 | if loaded is not None: |
|
99 | if loaded is not None: | |
@@ -83,13 +108,22 b' def get_options():' | |||||
83 | qt_api = os.environ.get('QT_API', None) |
|
108 | qt_api = os.environ.get('QT_API', None) | |
84 | if qt_api is None: |
|
109 | if qt_api is None: | |
85 | #no ETS variable. Ask mpl, then use default fallback path |
|
110 | #no ETS variable. Ask mpl, then use default fallback path | |
86 |
return matplotlib_options(mpl) or [ |
|
111 | return matplotlib_options(mpl) or [ | |
87 | QT_API_PYQT5, QT_API_PYSIDE2] |
|
112 | QT_API_PYQT_DEFAULT, | |
|
113 | QT_API_PYQT6, | |||
|
114 | QT_API_PYSIDE6, | |||
|
115 | QT_API_PYQT5, | |||
|
116 | QT_API_PYSIDE2, | |||
|
117 | QT_API_PYQT, | |||
|
118 | QT_API_PYSIDE, | |||
|
119 | ] | |||
88 | elif qt_api not in _qt_apis: |
|
120 | elif qt_api not in _qt_apis: | |
89 | raise RuntimeError("Invalid Qt API %r, valid values are: %r" % |
|
121 | raise RuntimeError("Invalid Qt API %r, valid values are: %r" % | |
90 | (qt_api, ', '.join(_qt_apis))) |
|
122 | (qt_api, ', '.join(_qt_apis))) | |
91 | else: |
|
123 | else: | |
92 | return [qt_api] |
|
124 | return [qt_api] | |
93 |
|
125 | |||
|
126 | ||||
94 | api_opts = get_options() |
|
127 | api_opts = get_options() | |
95 | QtCore, QtGui, QtSvg, QT_API = load_qt(api_opts) |
|
128 | QtCore, QtGui, QtSvg, QT_API = load_qt(api_opts) | |
|
129 | enum_helper = enum_factory(QT_API, QtCore) |
@@ -10,26 +10,41 b' 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, lru_cache | |
14 | from importlib import import_module |
|
14 | import operator | |
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 | # Qt6 | |
|
20 | QT_API_PYQT6 = "pyqt6" | |||
|
21 | QT_API_PYSIDE6 = "pyside6" | |||
|
22 | ||||
|
23 | # Qt5 | |||
20 | QT_API_PYQT5 = 'pyqt5' |
|
24 | QT_API_PYQT5 = 'pyqt5' | |
21 | QT_API_PYQTv1 = 'pyqtv1' # Force version 2 |
|
|||
22 | QT_API_PYQT_DEFAULT = 'pyqtdefault' # use system default for version 1 vs. 2 |
|
|||
23 | QT_API_PYSIDE = 'pyside' |
|
|||
24 | QT_API_PYSIDE2 = 'pyside2' |
|
25 | QT_API_PYSIDE2 = 'pyside2' | |
25 |
|
26 | |||
26 | api_to_module = {QT_API_PYSIDE2: 'PySide2', |
|
27 | # Qt4 | |
27 | QT_API_PYSIDE: 'PySide', |
|
28 | QT_API_PYQT = "pyqt" # Force version 2 | |
28 | QT_API_PYQT: 'PyQt4', |
|
29 | QT_API_PYQTv1 = "pyqtv1" # Force version 2 | |
29 | QT_API_PYQTv1: 'PyQt4', |
|
30 | QT_API_PYSIDE = "pyside" | |
30 | QT_API_PYQT5: 'PyQt5', |
|
31 | ||
31 | QT_API_PYQT_DEFAULT: 'PyQt4', |
|
32 | QT_API_PYQT_DEFAULT = "pyqtdefault" # use system default for version 1 vs. 2 | |
32 | } |
|
33 | ||
|
34 | api_to_module = { | |||
|
35 | # Qt6 | |||
|
36 | QT_API_PYQT6: "PyQt6", | |||
|
37 | QT_API_PYSIDE6: "PySide6", | |||
|
38 | # Qt5 | |||
|
39 | QT_API_PYQT5: "PyQt5", | |||
|
40 | QT_API_PYSIDE2: "PySide2", | |||
|
41 | # Qt4 | |||
|
42 | QT_API_PYSIDE: "PySide", | |||
|
43 | QT_API_PYQT: "PyQt4", | |||
|
44 | QT_API_PYQTv1: "PyQt4", | |||
|
45 | # default | |||
|
46 | QT_API_PYQT_DEFAULT: "PyQt6", | |||
|
47 | } | |||
33 |
|
48 | |||
34 |
|
49 | |||
35 | class ImportDenier(object): |
|
50 | class ImportDenier(object): | |
@@ -56,6 +71,7 b' class ImportDenier(object):' | |||||
56 | already imported an Incompatible QT Binding: %s |
|
71 | already imported an Incompatible QT Binding: %s | |
57 | """ % (fullname, loaded_api())) |
|
72 | """ % (fullname, loaded_api())) | |
58 |
|
73 | |||
|
74 | ||||
59 | ID = ImportDenier() |
|
75 | ID = ImportDenier() | |
60 | sys.meta_path.insert(0, ID) |
|
76 | sys.meta_path.insert(0, ID) | |
61 |
|
77 | |||
@@ -63,23 +79,11 b' sys.meta_path.insert(0, ID)' | |||||
63 | def commit_api(api): |
|
79 | def commit_api(api): | |
64 | """Commit to a particular API, and trigger ImportErrors on subsequent |
|
80 | """Commit to a particular API, and trigger ImportErrors on subsequent | |
65 | dangerous imports""" |
|
81 | dangerous imports""" | |
|
82 | modules = set(api_to_module.values()) | |||
66 |
|
83 | |||
67 | if api == QT_API_PYSIDE2: |
|
84 | modules.remove(api_to_module[api]) | |
68 | ID.forbid('PySide') |
|
85 | for mod in modules: | |
69 |
ID.forbid( |
|
86 | ID.forbid(mod) | |
70 | ID.forbid('PyQt5') |
|
|||
71 | elif api == QT_API_PYSIDE: |
|
|||
72 | ID.forbid('PySide2') |
|
|||
73 | ID.forbid('PyQt4') |
|
|||
74 | ID.forbid('PyQt5') |
|
|||
75 | elif api == QT_API_PYQT5: |
|
|||
76 | ID.forbid('PySide2') |
|
|||
77 | ID.forbid('PySide') |
|
|||
78 | ID.forbid('PyQt4') |
|
|||
79 | else: # There are three other possibilities, all representing PyQt4 |
|
|||
80 | ID.forbid('PyQt5') |
|
|||
81 | ID.forbid('PySide2') |
|
|||
82 | ID.forbid('PySide') |
|
|||
83 |
|
87 | |||
84 |
|
88 | |||
85 | def loaded_api(): |
|
89 | def loaded_api(): | |
@@ -90,19 +94,24 b' def loaded_api():' | |||||
90 |
|
94 | |||
91 | Returns |
|
95 | Returns | |
92 | ------- |
|
96 | ------- | |
93 |
None, 'pyside2', 'pyside', 'pyqt', 'pyqt5', |
|
97 | None, 'pyside6', 'pyqt6', 'pyside2', 'pyside', 'pyqt', 'pyqt5', 'pyqtv1' | |
94 | """ |
|
98 | """ | |
95 | if 'PyQt4.QtCore' in sys.modules: |
|
99 | if sys.modules.get("PyQt6.QtCore"): | |
|
100 | return QT_API_PYQT6 | |||
|
101 | elif sys.modules.get("PySide6.QtCore"): | |||
|
102 | return QT_API_PYSIDE6 | |||
|
103 | elif sys.modules.get("PyQt5.QtCore"): | |||
|
104 | return QT_API_PYQT5 | |||
|
105 | elif sys.modules.get("PySide2.QtCore"): | |||
|
106 | return QT_API_PYSIDE2 | |||
|
107 | elif sys.modules.get("PyQt4.QtCore"): | |||
96 | if qtapi_version() == 2: |
|
108 | if qtapi_version() == 2: | |
97 | return QT_API_PYQT |
|
109 | return QT_API_PYQT | |
98 | else: |
|
110 | else: | |
99 | return QT_API_PYQTv1 |
|
111 | return QT_API_PYQTv1 | |
100 | elif 'PySide.QtCore' in sys.modules: |
|
112 | elif sys.modules.get("PySide.QtCore"): | |
101 | return QT_API_PYSIDE |
|
113 | return QT_API_PYSIDE | |
102 | elif 'PySide2.QtCore' in sys.modules: |
|
114 | ||
103 | return QT_API_PYSIDE2 |
|
|||
104 | elif 'PyQt5.QtCore' in sys.modules: |
|
|||
105 | return QT_API_PYQT5 |
|
|||
106 | return None |
|
115 | return None | |
107 |
|
116 | |||
108 |
|
117 | |||
@@ -122,7 +131,7 b' def has_binding(api):' | |||||
122 | from importlib.util import find_spec |
|
131 | from importlib.util import find_spec | |
123 |
|
132 | |||
124 | required = ['QtCore', 'QtGui', 'QtSvg'] |
|
133 | required = ['QtCore', 'QtGui', 'QtSvg'] | |
125 | if api in (QT_API_PYQT5, QT_API_PYSIDE2): |
|
134 | if api in (QT_API_PYQT5, QT_API_PYSIDE2, QT_API_PYQT6, QT_API_PYSIDE6): | |
126 | # QT5 requires QtWidgets too |
|
135 | # QT5 requires QtWidgets too | |
127 | required.append('QtWidgets') |
|
136 | required.append('QtWidgets') | |
128 |
|
137 | |||
@@ -174,7 +183,7 b' def can_import(api):' | |||||
174 |
|
183 | |||
175 | current = loaded_api() |
|
184 | current = loaded_api() | |
176 | if api == QT_API_PYQT_DEFAULT: |
|
185 | if api == QT_API_PYQT_DEFAULT: | |
177 |
return current in [QT_API_PYQT |
|
186 | return current in [QT_API_PYQT6, None] | |
178 | else: |
|
187 | else: | |
179 | return current in [api, None] |
|
188 | return current in [api, None] | |
180 |
|
189 | |||
@@ -224,7 +233,7 b' def import_pyqt5():' | |||||
224 | """ |
|
233 | """ | |
225 |
|
234 | |||
226 | from PyQt5 import QtCore, QtSvg, QtWidgets, QtGui |
|
235 | from PyQt5 import QtCore, QtSvg, QtWidgets, QtGui | |
227 |
|
236 | |||
228 | # Alias PyQt-specific functions for PySide compatibility. |
|
237 | # Alias PyQt-specific functions for PySide compatibility. | |
229 | QtCore.Signal = QtCore.pyqtSignal |
|
238 | QtCore.Signal = QtCore.pyqtSignal | |
230 | QtCore.Slot = QtCore.pyqtSlot |
|
239 | QtCore.Slot = QtCore.pyqtSlot | |
@@ -238,6 +247,28 b' def import_pyqt5():' | |||||
238 | return QtCore, QtGuiCompat, QtSvg, api |
|
247 | return QtCore, QtGuiCompat, QtSvg, api | |
239 |
|
248 | |||
240 |
|
249 | |||
|
250 | def import_pyqt6(): | |||
|
251 | """ | |||
|
252 | Import PyQt6 | |||
|
253 | ||||
|
254 | ImportErrors rasied within this function are non-recoverable | |||
|
255 | """ | |||
|
256 | ||||
|
257 | from PyQt6 import QtCore, QtSvg, QtWidgets, QtGui | |||
|
258 | ||||
|
259 | # Alias PyQt-specific functions for PySide compatibility. | |||
|
260 | QtCore.Signal = QtCore.pyqtSignal | |||
|
261 | QtCore.Slot = QtCore.pyqtSlot | |||
|
262 | ||||
|
263 | # Join QtGui and QtWidgets for Qt4 compatibility. | |||
|
264 | QtGuiCompat = types.ModuleType("QtGuiCompat") | |||
|
265 | QtGuiCompat.__dict__.update(QtGui.__dict__) | |||
|
266 | QtGuiCompat.__dict__.update(QtWidgets.__dict__) | |||
|
267 | ||||
|
268 | api = QT_API_PYQT6 | |||
|
269 | return QtCore, QtGuiCompat, QtSvg, api | |||
|
270 | ||||
|
271 | ||||
241 | def import_pyside(): |
|
272 | def import_pyside(): | |
242 | """ |
|
273 | """ | |
243 | Import PySide |
|
274 | Import PySide | |
@@ -264,6 +295,23 b' def import_pyside2():' | |||||
264 | return QtCore, QtGuiCompat, QtSvg, QT_API_PYSIDE2 |
|
295 | return QtCore, QtGuiCompat, QtSvg, QT_API_PYSIDE2 | |
265 |
|
296 | |||
266 |
|
297 | |||
|
298 | def import_pyside6(): | |||
|
299 | """ | |||
|
300 | Import PySide6 | |||
|
301 | ||||
|
302 | ImportErrors raised within this function are non-recoverable | |||
|
303 | """ | |||
|
304 | from PySide6 import QtGui, QtCore, QtSvg, QtWidgets, QtPrintSupport | |||
|
305 | ||||
|
306 | # Join QtGui and QtWidgets for Qt4 compatibility. | |||
|
307 | QtGuiCompat = types.ModuleType("QtGuiCompat") | |||
|
308 | QtGuiCompat.__dict__.update(QtGui.__dict__) | |||
|
309 | QtGuiCompat.__dict__.update(QtWidgets.__dict__) | |||
|
310 | QtGuiCompat.__dict__.update(QtPrintSupport.__dict__) | |||
|
311 | ||||
|
312 | return QtCore, QtGuiCompat, QtSvg, QT_API_PYSIDE6 | |||
|
313 | ||||
|
314 | ||||
267 | def load_qt(api_options): |
|
315 | def load_qt(api_options): | |
268 | """ |
|
316 | """ | |
269 | Attempt to import Qt, given a preference list |
|
317 | Attempt to import Qt, given a preference list | |
@@ -291,13 +339,19 b' def load_qt(api_options):' | |||||
291 | an incompatible library has already been installed) |
|
339 | an incompatible library has already been installed) | |
292 | """ |
|
340 | """ | |
293 | loaders = { |
|
341 | loaders = { | |
294 | QT_API_PYSIDE2: import_pyside2, |
|
342 | # Qt6 | |
295 |
|
|
343 | QT_API_PYQT6: import_pyqt6, | |
296 |
|
|
344 | QT_API_PYSIDE6: import_pyside6, | |
297 | QT_API_PYQT5: import_pyqt5, |
|
345 | # Qt5 | |
298 |
|
|
346 | QT_API_PYQT5: import_pyqt5, | |
299 |
|
|
347 | QT_API_PYSIDE2: import_pyside2, | |
300 |
|
|
348 | # Qt4 | |
|
349 | QT_API_PYSIDE: import_pyside, | |||
|
350 | QT_API_PYQT: import_pyqt4, | |||
|
351 | QT_API_PYQTv1: partial(import_pyqt4, version=1), | |||
|
352 | # default | |||
|
353 | QT_API_PYQT_DEFAULT: import_pyqt6, | |||
|
354 | } | |||
301 |
|
355 | |||
302 | for api in api_options: |
|
356 | for api in api_options: | |
303 |
|
357 | |||
@@ -332,3 +386,16 b' def load_qt(api_options):' | |||||
332 | has_binding(QT_API_PYSIDE), |
|
386 | has_binding(QT_API_PYSIDE), | |
333 | has_binding(QT_API_PYSIDE2), |
|
387 | has_binding(QT_API_PYSIDE2), | |
334 | api_options)) |
|
388 | api_options)) | |
|
389 | ||||
|
390 | ||||
|
391 | def enum_factory(QT_API, QtCore): | |||
|
392 | """Construct an enum helper to account for PyQt5 <-> PyQt6 changes.""" | |||
|
393 | ||||
|
394 | @lru_cache(None) | |||
|
395 | def _enum(name): | |||
|
396 | # foo.bar.Enum.Entry (PyQt6) <=> foo.bar.Entry (non-PyQt6). | |||
|
397 | return operator.attrgetter( | |||
|
398 | name if QT_API == QT_API_PYQT6 else name.rpartition(".")[0] | |||
|
399 | )(sys.modules[QtCore.__package__]) | |||
|
400 | ||||
|
401 | return _enum |
@@ -7,13 +7,19 b' aliases = {' | |||||
7 | } |
|
7 | } | |
8 |
|
8 | |||
9 | backends = [ |
|
9 | backends = [ | |
10 | 'qt', 'qt4', 'qt5', |
|
10 | "qt", | |
11 | 'gtk', 'gtk2', 'gtk3', |
|
11 | "qt4", | |
12 |
|
|
12 | "qt5", | |
13 | 'wx', |
|
13 | "qt6", | |
14 | 'pyglet', 'glut', |
|
14 | "gtk", | |
15 | 'osx', |
|
15 | "gtk2", | |
16 | 'asyncio' |
|
16 | "gtk3", | |
|
17 | "tk", | |||
|
18 | "wx", | |||
|
19 | "pyglet", | |||
|
20 | "glut", | |||
|
21 | "osx", | |||
|
22 | "asyncio", | |||
17 | ] |
|
23 | ] | |
18 |
|
24 | |||
19 | registered = {} |
|
25 | registered = {} | |
@@ -22,6 +28,7 b' def register(name, inputhook):' | |||||
22 | """Register the function *inputhook* as an event loop integration.""" |
|
28 | """Register the function *inputhook* as an event loop integration.""" | |
23 | registered[name] = inputhook |
|
29 | registered[name] = inputhook | |
24 |
|
30 | |||
|
31 | ||||
25 | class UnknownBackend(KeyError): |
|
32 | class UnknownBackend(KeyError): | |
26 | def __init__(self, name): |
|
33 | def __init__(self, name): | |
27 | self.name = name |
|
34 | self.name = name | |
@@ -31,6 +38,7 b' class UnknownBackend(KeyError):' | |||||
31 | "Supported event loops are: {}").format(self.name, |
|
38 | "Supported event loops are: {}").format(self.name, | |
32 | ', '.join(backends + sorted(registered))) |
|
39 | ', '.join(backends + sorted(registered))) | |
33 |
|
40 | |||
|
41 | ||||
34 | def get_inputhook_name_and_func(gui): |
|
42 | def get_inputhook_name_and_func(gui): | |
35 | if gui in registered: |
|
43 | if gui in registered: | |
36 | return gui, registered[gui] |
|
44 | return gui, registered[gui] | |
@@ -42,9 +50,12 b' def get_inputhook_name_and_func(gui):' | |||||
42 | return get_inputhook_name_and_func(aliases[gui]) |
|
50 | return get_inputhook_name_and_func(aliases[gui]) | |
43 |
|
51 | |||
44 | gui_mod = gui |
|
52 | gui_mod = gui | |
45 |
if gui == |
|
53 | if gui == "qt5": | |
46 |
os.environ[ |
|
54 | os.environ["QT_API"] = "pyqt5" | |
47 |
gui_mod = |
|
55 | gui_mod = "qt" | |
|
56 | elif gui == "qt6": | |||
|
57 | os.environ["QT_API"] = "pyqt6" | |||
|
58 | gui_mod = "qt" | |||
48 |
|
59 | |||
49 | mod = importlib.import_module('IPython.terminal.pt_inputhooks.'+gui_mod) |
|
60 | mod = importlib.import_module('IPython.terminal.pt_inputhooks.'+gui_mod) | |
50 | return gui, mod.inputhook |
|
61 | return gui, mod.inputhook |
@@ -1,6 +1,6 b'' | |||||
1 | import sys |
|
1 | import sys | |
2 | import os |
|
2 | import os | |
3 | from IPython.external.qt_for_kernel import QtCore, QtGui |
|
3 | from IPython.external.qt_for_kernel import QtCore, QtGui, enum_helper | |
4 | from IPython import get_ipython |
|
4 | from IPython import get_ipython | |
5 |
|
5 | |||
6 | # If we create a QApplication, keep a reference to it so that it doesn't get |
|
6 | # If we create a QApplication, keep a reference to it so that it doesn't get | |
@@ -9,6 +9,11 b' _appref = None' | |||||
9 | _already_warned = False |
|
9 | _already_warned = False | |
10 |
|
10 | |||
11 |
|
11 | |||
|
12 | def _exec(obj): | |||
|
13 | # exec on PyQt6, exec_ elsewhere. | |||
|
14 | obj.exec() if hasattr(obj, "exec") else obj.exec_() | |||
|
15 | ||||
|
16 | ||||
12 | def _reclaim_excepthook(): |
|
17 | def _reclaim_excepthook(): | |
13 | shell = get_ipython() |
|
18 | shell = get_ipython() | |
14 | if shell is not None: |
|
19 | if shell is not None: | |
@@ -32,7 +37,16 b' def inputhook(context):' | |||||
32 | 'variable. Deactivate Qt5 code.' |
|
37 | 'variable. Deactivate Qt5 code.' | |
33 | ) |
|
38 | ) | |
34 | return |
|
39 | return | |
35 | QtCore.QCoreApplication.setAttribute(QtCore.Qt.AA_EnableHighDpiScaling) |
|
40 | try: | |
|
41 | QtCore.QApplication.setAttribute(QtCore.Qt.AA_EnableHighDpiScaling) | |||
|
42 | except AttributeError: # Only for Qt>=5.6, <6. | |||
|
43 | pass | |||
|
44 | try: | |||
|
45 | QtCore.QApplication.setHighDpiScaleFactorRoundingPolicy( | |||
|
46 | QtCore.Qt.HighDpiScaleFactorRoundingPolicy.PassThrough | |||
|
47 | ) | |||
|
48 | except AttributeError: # Only for Qt>=5.14. | |||
|
49 | pass | |||
36 | _appref = app = QtGui.QApplication([" "]) |
|
50 | _appref = app = QtGui.QApplication([" "]) | |
37 |
|
51 | |||
38 | # "reclaim" IPython sys.excepthook after event loop starts |
|
52 | # "reclaim" IPython sys.excepthook after event loop starts | |
@@ -55,14 +69,15 b' def inputhook(context):' | |||||
55 | else: |
|
69 | else: | |
56 | # On POSIX platforms, we can use a file descriptor to quit the event |
|
70 | # On POSIX platforms, we can use a file descriptor to quit the event | |
57 | # loop when there is input ready to read. |
|
71 | # loop when there is input ready to read. | |
58 |
notifier = QtCore.QSocketNotifier( |
|
72 | notifier = QtCore.QSocketNotifier( | |
59 | QtCore.QSocketNotifier.Read) |
|
73 | context.fileno(), enum_helper("QtCore.QSocketNotifier.Type").Read | |
|
74 | ) | |||
60 | try: |
|
75 | try: | |
61 | # connect the callback we care about before we turn it on |
|
76 | # connect the callback we care about before we turn it on | |
62 | notifier.activated.connect(lambda: event_loop.exit()) |
|
77 | notifier.activated.connect(lambda: event_loop.exit()) | |
63 | notifier.setEnabled(True) |
|
78 | notifier.setEnabled(True) | |
64 | # only start the event loop we are not already flipped |
|
79 | # only start the event loop we are not already flipped | |
65 | if not context.input_is_ready(): |
|
80 | if not context.input_is_ready(): | |
66 |
event_loop |
|
81 | _exec(event_loop) | |
67 | finally: |
|
82 | finally: | |
68 | notifier.setEnabled(False) |
|
83 | notifier.setEnabled(False) |
General Comments 0
You need to be logged in to leave comments.
Login now