Show More
@@ -3657,7 +3657,7 b' class InteractiveShell(SingletonConfigurable):' | |||
|
3657 | 3657 | from IPython.core import pylabtools as pt |
|
3658 | 3658 | gui, backend = pt.find_gui_and_backend(gui, self.pylab_gui_select) |
|
3659 | 3659 | |
|
3660 |
if gui != |
|
|
3660 | if gui != None: | |
|
3661 | 3661 | # If we have our first gui selection, store it |
|
3662 | 3662 | if self.pylab_gui_select is None: |
|
3663 | 3663 | self.pylab_gui_select = gui |
@@ -18,7 +18,6 b' from IPython.core import magic_arguments' | |||
|
18 | 18 | from IPython.core.magic import Magics, magics_class, line_magic |
|
19 | 19 | from IPython.testing.skipdoctest import skip_doctest |
|
20 | 20 | from warnings import warn |
|
21 | from IPython.core.pylabtools import backends | |
|
22 | 21 | |
|
23 | 22 | #----------------------------------------------------------------------------- |
|
24 | 23 | # Magic implementation classes |
@@ -26,11 +25,11 b' from IPython.core.pylabtools import backends' | |||
|
26 | 25 | |
|
27 | 26 | magic_gui_arg = magic_arguments.argument( |
|
28 | 27 | 'gui', nargs='?', |
|
29 |
help="""Name of the matplotlib backend to use |
|
|
28 | help="""Name of the matplotlib backend to use such as 'qt' or 'widget'. | |
|
30 | 29 | If given, the corresponding matplotlib backend is used, |
|
31 | 30 | otherwise it will be matplotlib's default |
|
32 | 31 | (which you can set in your matplotlib config file). |
|
33 | """ % str(tuple(sorted(backends.keys()))) | |
|
32 | """ | |
|
34 | 33 | ) |
|
35 | 34 | |
|
36 | 35 | |
@@ -93,7 +92,13 b' class PylabMagics(Magics):' | |||
|
93 | 92 | """ |
|
94 | 93 | args = magic_arguments.parse_argstring(self.matplotlib, line) |
|
95 | 94 | if args.list: |
|
96 | backends_list = list(backends.keys()) | |
|
95 | from IPython.core.pylabtools import _matplotlib_manages_backends | |
|
96 | if _matplotlib_manages_backends(): | |
|
97 | from matplotlib.backends.registry import backend_registry | |
|
98 | backends_list = backend_registry.list_all() | |
|
99 | else: | |
|
100 | from IPython.core.pylabtools import backends | |
|
101 | backends_list = list(backends.keys()) | |
|
97 | 102 | print("Available matplotlib backends: %s" % backends_list) |
|
98 | 103 | else: |
|
99 | 104 | gui, backend = self.shell.enable_matplotlib(args.gui.lower() if isinstance(args.gui, str) else args.gui) |
@@ -12,9 +12,12 b' import warnings' | |||
|
12 | 12 | from IPython.core.display import _pngxy |
|
13 | 13 | from IPython.utils.decorators import flag_calls |
|
14 | 14 | |
|
15 | # If user specifies a GUI, that dictates the backend, otherwise we read the | |
|
16 | # user's mpl default from the mpl rc structure | |
|
17 | backends = { | |
|
15 | ||
|
16 | # Matplotlib backend resolution functionality moved from IPython to Matplotlib | |
|
17 | # in IPython 8.23 and Matplotlib 3.9. Need to keep `backends` and `backend2gui` | |
|
18 | # here for earlier Matplotlib and for external backend libraries such as | |
|
19 | # mplcairo that might rely upon it. | |
|
20 | _deprecated_backends = { | |
|
18 | 21 | "tk": "TkAgg", |
|
19 | 22 | "gtk": "GTKAgg", |
|
20 | 23 | "gtk3": "GTK3Agg", |
@@ -41,29 +44,38 b' backends = {' | |||
|
41 | 44 | # GUI support to activate based on the desired matplotlib backend. For the |
|
42 | 45 | # most part it's just a reverse of the above dict, but we also need to add a |
|
43 | 46 | # few others that map to the same GUI manually: |
|
44 | backend2gui = dict(zip(backends.values(), backends.keys())) | |
|
47 | _deprecated_backend2gui = dict(zip(_deprecated_backends.values(), _deprecated_backends.keys())) | |
|
45 | 48 | # In the reverse mapping, there are a few extra valid matplotlib backends that |
|
46 | 49 | # map to the same GUI support |
|
47 | backend2gui["GTK"] = backend2gui["GTKCairo"] = "gtk" | |
|
48 | backend2gui["GTK3Cairo"] = "gtk3" | |
|
49 | backend2gui["GTK4Cairo"] = "gtk4" | |
|
50 | backend2gui["WX"] = "wx" | |
|
51 | backend2gui["CocoaAgg"] = "osx" | |
|
50 | _deprecated_backend2gui["GTK"] = _deprecated_backend2gui["GTKCairo"] = "gtk" | |
|
51 | _deprecated_backend2gui["GTK3Cairo"] = "gtk3" | |
|
52 | _deprecated_backend2gui["GTK4Cairo"] = "gtk4" | |
|
53 | _deprecated_backend2gui["WX"] = "wx" | |
|
54 | _deprecated_backend2gui["CocoaAgg"] = "osx" | |
|
52 | 55 | # There needs to be a hysteresis here as the new QtAgg Matplotlib backend |
|
53 | 56 | # supports either Qt5 or Qt6 and the IPython qt event loop support Qt4, Qt5, |
|
54 | 57 | # and Qt6. |
|
55 | backend2gui["QtAgg"] = "qt" | |
|
56 | backend2gui["Qt4Agg"] = "qt4" | |
|
57 | backend2gui["Qt5Agg"] = "qt5" | |
|
58 | _deprecated_backend2gui["QtAgg"] = "qt" | |
|
59 | _deprecated_backend2gui["Qt4Agg"] = "qt4" | |
|
60 | _deprecated_backend2gui["Qt5Agg"] = "qt5" | |
|
58 | 61 | |
|
59 | 62 | # And some backends that don't need GUI integration |
|
60 | del backend2gui["nbAgg"] | |
|
61 | del backend2gui["agg"] | |
|
62 | del backend2gui["svg"] | |
|
63 | del backend2gui["pdf"] | |
|
64 | del backend2gui["ps"] | |
|
65 | del backend2gui["module://matplotlib_inline.backend_inline"] | |
|
66 | del backend2gui["module://ipympl.backend_nbagg"] | |
|
63 | del _deprecated_backend2gui["nbAgg"] | |
|
64 | del _deprecated_backend2gui["agg"] | |
|
65 | del _deprecated_backend2gui["svg"] | |
|
66 | del _deprecated_backend2gui["pdf"] | |
|
67 | del _deprecated_backend2gui["ps"] | |
|
68 | del _deprecated_backend2gui["module://matplotlib_inline.backend_inline"] | |
|
69 | del _deprecated_backend2gui["module://ipympl.backend_nbagg"] | |
|
70 | ||
|
71 | ||
|
72 | # Deprecated attributes backends and backend2gui mostly following PEP 562. | |
|
73 | def __getattr__(name): | |
|
74 | if name in ("backends", "backend2gui"): | |
|
75 | warnings.warn(f"{name} is deprecated", DeprecationWarning) | |
|
76 | return globals()[f"_deprecated_{name}"] | |
|
77 | raise AttributeError(f"module {__name__!r} has no attribute {name!r}") | |
|
78 | ||
|
67 | 79 | |
|
68 | 80 | #----------------------------------------------------------------------------- |
|
69 | 81 | # Matplotlib utilities |
@@ -267,7 +279,7 b' def select_figure_formats(shell, formats, **kwargs):' | |||
|
267 | 279 | |
|
268 | 280 | [ f.pop(Figure, None) for f in shell.display_formatter.formatters.values() ] |
|
269 | 281 | mplbackend = matplotlib.get_backend().lower() |
|
270 |
if mplbackend |
|
|
282 | if mplbackend in ('nbagg', 'ipympl', 'widget', 'module://ipympl.backend_nbagg'): | |
|
271 | 283 | formatter = shell.display_formatter.ipython_display_formatter |
|
272 | 284 | formatter.for_type(Figure, _reshow_nbagg_figure) |
|
273 | 285 | |
@@ -318,9 +330,23 b' def find_gui_and_backend(gui=None, gui_select=None):' | |||
|
318 | 330 | """ |
|
319 | 331 | |
|
320 | 332 | import matplotlib |
|
333 | if _matplotlib_manages_backends(): | |
|
334 | backend_registry = matplotlib.backends.registry.backend_registry | |
|
335 | ||
|
336 | # gui argument may be a gui event loop or may be a backend name. | |
|
337 | if gui in ("auto", None): | |
|
338 | backend = matplotlib.rcParamsOrig['backend'] | |
|
339 | backend, gui = backend_registry.resolve_backend(backend) | |
|
340 | else: | |
|
341 | backend, gui = backend_registry.resolve_gui_or_backend(gui) | |
|
321 | 342 | |
|
322 | has_unified_qt_backend = getattr(matplotlib, "__version_info__", (0, 0)) >= (3, 5) | |
|
343 | return gui, backend | |
|
323 | 344 | |
|
345 | # Fallback to previous behaviour (Matplotlib < 3.9) | |
|
346 | mpl_version_info = getattr(matplotlib, "__version_info__", (0, 0)) | |
|
347 | has_unified_qt_backend = mpl_version_info >= (3, 5) | |
|
348 | ||
|
349 | from IPython.core.pylabtools import backends | |
|
324 | 350 | backends_ = dict(backends) |
|
325 | 351 | if not has_unified_qt_backend: |
|
326 | 352 | backends_["qt"] = "qt5agg" |
@@ -338,6 +364,7 b' def find_gui_and_backend(gui=None, gui_select=None):' | |||
|
338 | 364 | backend = matplotlib.rcParamsOrig['backend'] |
|
339 | 365 | # In this case, we need to find what the appropriate gui selection call |
|
340 | 366 | # should be for IPython, so we can activate inputhook accordingly |
|
367 | from IPython.core.pylabtools import backend2gui | |
|
341 | 368 | gui = backend2gui.get(backend, None) |
|
342 | 369 | |
|
343 | 370 | # If we have already had a gui active, we need it and inline are the |
@@ -346,6 +373,10 b' def find_gui_and_backend(gui=None, gui_select=None):' | |||
|
346 | 373 | gui = gui_select |
|
347 | 374 | backend = backends_[gui] |
|
348 | 375 | |
|
376 | # Since IPython 8.23.0 use None for no gui event loop rather than "inline". | |
|
377 | if gui == "inline": | |
|
378 | gui = None | |
|
379 | ||
|
349 | 380 | return gui, backend |
|
350 | 381 | |
|
351 | 382 | |
@@ -431,3 +462,9 b' def configure_inline_support(shell, backend):' | |||
|
431 | 462 | ) |
|
432 | 463 | |
|
433 | 464 | configure_inline_support_orig(shell, backend) |
|
465 | ||
|
466 | ||
|
467 | def _matplotlib_manages_backends(): | |
|
468 | import matplotlib | |
|
469 | mpl_version_info = getattr(matplotlib, "__version_info__", (0, 0)) | |
|
470 | return mpl_version_info >= (3, 9) |
@@ -31,8 +31,7 b' from IPython.terminal import pt_inputhooks' | |||
|
31 | 31 | |
|
32 | 32 | gui_keys = tuple(sorted(pt_inputhooks.backends) + sorted(pt_inputhooks.aliases)) |
|
33 | 33 | |
|
34 | backend_keys = sorted(pylabtools.backends.keys()) | |
|
35 | backend_keys.insert(0, 'auto') | |
|
34 | backend_keys = [] | |
|
36 | 35 | |
|
37 | 36 | shell_flags = {} |
|
38 | 37 |
@@ -199,7 +199,7 b' class TestPylabSwitch(object):' | |||
|
199 | 199 | assert s.pylab_gui_select == "qt" |
|
200 | 200 | |
|
201 | 201 | gui, backend = s.enable_matplotlib("inline") |
|
202 |
assert gui |
|
|
202 | assert gui is None | |
|
203 | 203 | assert s.pylab_gui_select == "qt" |
|
204 | 204 | |
|
205 | 205 | gui, backend = s.enable_matplotlib("qt") |
@@ -207,7 +207,7 b' class TestPylabSwitch(object):' | |||
|
207 | 207 | assert s.pylab_gui_select == "qt" |
|
208 | 208 | |
|
209 | 209 | gui, backend = s.enable_matplotlib("inline") |
|
210 |
assert gui |
|
|
210 | assert gui is None | |
|
211 | 211 | assert s.pylab_gui_select == "qt" |
|
212 | 212 | |
|
213 | 213 | gui, backend = s.enable_matplotlib() |
@@ -217,11 +217,11 b' class TestPylabSwitch(object):' | |||
|
217 | 217 | def test_inline(self): |
|
218 | 218 | s = self.Shell() |
|
219 | 219 | gui, backend = s.enable_matplotlib("inline") |
|
220 |
assert gui |
|
|
220 | assert gui is None | |
|
221 | 221 | assert s.pylab_gui_select == None |
|
222 | 222 | |
|
223 | 223 | gui, backend = s.enable_matplotlib("inline") |
|
224 |
assert gui |
|
|
224 | assert gui is None | |
|
225 | 225 | assert s.pylab_gui_select == None |
|
226 | 226 | |
|
227 | 227 | gui, backend = s.enable_matplotlib("qt") |
@@ -233,14 +233,14 b' class TestPylabSwitch(object):' | |||
|
233 | 233 | |
|
234 | 234 | ip = self.Shell() |
|
235 | 235 | gui, backend = ip.enable_matplotlib("inline") |
|
236 |
assert gui |
|
|
236 | assert gui is None | |
|
237 | 237 | |
|
238 | 238 | fmts = {'png'} |
|
239 | 239 | active_mimes = {_fmt_mime_map[fmt] for fmt in fmts} |
|
240 | 240 | pt.select_figure_formats(ip, fmts) |
|
241 | 241 | |
|
242 | 242 | gui, backend = ip.enable_matplotlib("inline") |
|
243 |
assert gui |
|
|
243 | assert gui is None | |
|
244 | 244 | |
|
245 | 245 | for mime, f in ip.display_formatter.formatters.items(): |
|
246 | 246 | if mime in active_mimes: |
@@ -254,7 +254,7 b' class TestPylabSwitch(object):' | |||
|
254 | 254 | assert gui == "qt" |
|
255 | 255 | assert s.pylab_gui_select == "qt" |
|
256 | 256 | |
|
257 | gui, backend = s.enable_matplotlib("gtk") | |
|
257 | gui, backend = s.enable_matplotlib("gtk3") | |
|
258 | 258 | assert gui == "qt" |
|
259 | 259 | assert s.pylab_gui_select == "qt" |
|
260 | 260 |
@@ -966,7 +966,7 b' class TerminalInteractiveShell(InteractiveShell):' | |||
|
966 | 966 | if self._inputhook is not None and gui is None: |
|
967 | 967 | self.active_eventloop = self._inputhook = None |
|
968 | 968 | |
|
969 |
if gui and (gui not in { |
|
|
969 | if gui and (gui not in {None, "webagg"}): | |
|
970 | 970 | # This hook runs with each cycle of the `prompt_toolkit`'s event loop. |
|
971 | 971 | self.active_eventloop, self._inputhook = get_inputhook_name_and_func(gui) |
|
972 | 972 | else: |
General Comments 0
You need to be logged in to leave comments.
Login now