Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

PR: Fix Matplotlib interactive backend detection #485

Merged
merged 2 commits into from
May 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion spyder_kernels/comms/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,13 @@ def is_benign_message(self, message):
# Fixes spyder-ide/spyder-kernels#343
'DeprecationWarning',
# Fixes spyder-ide/spyder-kernels#365
'IOStream.flush timed out'
'IOStream.flush timed out',
# Avoid unnecessary messages from set_configuration when changing
# Matplotlib options.
"Warning: Cannot change to a different GUI toolkit",
"%pylab is deprecated",
"Populating the interactive namespace",
"\n"
]

return any([msg in message for msg in benign_messages])
Expand Down
46 changes: 13 additions & 33 deletions spyder_kernels/console/kernel.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@

# Third-party imports
from ipykernel.ipkernel import IPythonKernel
from ipykernel import eventloops, get_connection_info
from ipykernel import get_connection_info
from traitlets.config.loader import LazyConfigValue
import zmq
from zmq.utils.garbage import gc
Expand Down Expand Up @@ -76,6 +76,9 @@ def __init__(self, *args, **kwargs):
# Socket to signal shell_stream locally
self.loopback_socket = None

# To track the interactive backend
self.interactive_backend = None

@property
def kernel_info(self):
# Used for checking correct version by spyder
Expand Down Expand Up @@ -505,43 +508,20 @@ def get_mpl_interactive_backend(self):
Get current Matplotlib interactive backend.

This is different from the current backend because, for instance, the
user can set first the Qt5 backend, then the Inline one. In that case,
the current backend is Inline, but the current interactive one is Qt5,
user can set first the Qt backend, then the Inline one. In that case,
the current backend is Inline, but the current interactive one is Qt,
and this backend can't be changed without a kernel restart.
"""
# Mapping from frameworks to backend names.
mapping = {
'qt': 'QtAgg',
'tk': 'TkAgg',
'macosx': 'MacOSX'
}

# --- Get interactive framework
framework = None

# Detect if there is a graphical framework running by checking the
# eventloop function attached to the kernel.eventloop attribute (see
# `ipykernel.eventloops.enable_gui` for context).
loop_func = self.eventloop

if loop_func is not None:
if loop_func == eventloops.loop_tk:
framework = 'tk'
elif loop_func == eventloops.loop_qt5:
framework = 'qt'
elif loop_func == eventloops.loop_cocoa:
framework = 'macosx'
else:
# Spyder doesn't handle other backends
framework = 'other'
# Backends that Spyder can handle
recognized_backends = {'qt', 'tk', 'macosx'}

# --- Return backend according to framework
if framework is None:
# Since no interactive backend has been set yet, this is
# equivalent to having the inline one.
if self.interactive_backend is None:
# Since no interactive backend has been set yet, this is equivalent
# to having the inline one.
return 'inline'
elif framework in mapping:
return MPL_BACKENDS_TO_SPYDER[mapping[framework]]
elif self.interactive_backend in recognized_backends:
return self.interactive_backend
else:
# This covers the case of other backends (e.g. Wx or Gtk)
# which users can set interactively with the %matplotlib
Expand Down
15 changes: 14 additions & 1 deletion spyder_kernels/console/shell.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,14 +87,27 @@ def enable_matplotlib(self, gui=None):
"""Enable matplotlib."""
if gui is None or gui.lower() == "auto":
gui = automatic_backend()
gui, backend = super(SpyderShell, self).enable_matplotlib(gui)

enabled_gui, backend = super().enable_matplotlib(gui)

# This is necessary for IPython 8.24+, which returns None after
# enabling the Inline backend.
if enabled_gui is None and gui == "inline":
enabled_gui = "inline"
gui = enabled_gui

# To easily track the current interactive backend
if self.kernel.interactive_backend is None:
self.kernel.interactive_backend = gui if gui != "inline" else None

if self.update_gui_frontend:
try:
self.kernel.frontend_call(
blocking=False
).update_matplotlib_gui(gui)
except Exception:
pass

return gui, backend

# --- For Pdb namespace integration
Expand Down
Loading