From 59790c0998d44d80cbc3df10d38da8d8ede56b23 Mon Sep 17 00:00:00 2001 From: Pierre Raybaut Date: Wed, 16 Nov 2022 17:00:43 +0100 Subject: [PATCH 01/11] VarExp: new "Plotting library" option (default: matplotlib) --- .../spyder_kernels/console/start.py | 8 ++--- spyder/config/main.py | 3 +- .../ipythonconsole/utils/kernelspec.py | 1 + spyder/plugins/variableexplorer/confpage.py | 31 +++++++++++++++---- 4 files changed, 31 insertions(+), 12 deletions(-) diff --git a/external-deps/spyder-kernels/spyder_kernels/console/start.py b/external-deps/spyder-kernels/spyder_kernels/console/start.py index f5e4b122b0c..4b597fb06b7 100644 --- a/external-deps/spyder-kernels/spyder_kernels/console/start.py +++ b/external-deps/spyder-kernels/spyder_kernels/console/start.py @@ -254,11 +254,9 @@ def varexp(line): """ ip = get_ipython() #analysis:ignore funcname, name = line.split() - try: - import guiqwt.pyplot as pyplot - except: - import matplotlib.pyplot as pyplot - pyplot.figure(); + plotlib = os.environ.get('SPY_VAREXP_PLOTLIB') + pyplot = __import__(plotlib + '.pyplot', globals(), locals(), [], 0).pyplot + pyplot.figure() getattr(pyplot, funcname[2:])(ip.kernel._get_current_namespace()[name]) pyplot.show() diff --git a/spyder/config/main.py b/spyder/config/main.py index cd06e14ae92..12eb662af22 100644 --- a/spyder/config/main.py +++ b/spyder/config/main.py @@ -176,7 +176,8 @@ 'truncate': True, 'minmax': False, 'show_callable_attributes': True, - 'show_special_attributes': False + 'show_special_attributes': False, + 'plotlib': 'matplotlib', }), ('debugger', { diff --git a/spyder/plugins/ipythonconsole/utils/kernelspec.py b/spyder/plugins/ipythonconsole/utils/kernelspec.py index ae6ced09d71..af290dd25a8 100644 --- a/spyder/plugins/ipythonconsole/utils/kernelspec.py +++ b/spyder/plugins/ipythonconsole/utils/kernelspec.py @@ -208,6 +208,7 @@ def env(self): 'SPY_GREEDY_O': self.get_conf('greedy_completer'), 'SPY_JEDI_O': self.get_conf('jedi_completer'), 'SPY_SYMPY_O': self.get_conf('symbolic_math'), + 'SPY_VAREXP_PLOTLIB': self.get_conf('plotlib', section='variable_explorer'), 'SPY_TESTING': running_under_pytest() or get_safe_mode(), 'SPY_HIDE_CMD': self.get_conf('hide_cmd_windows'), 'SPY_PYTHONPATH': pypath diff --git a/spyder/plugins/variableexplorer/confpage.py b/spyder/plugins/variableexplorer/confpage.py index a2e7a8d0ecd..1bf3df78dca 100644 --- a/spyder/plugins/variableexplorer/confpage.py +++ b/spyder/plugins/variableexplorer/confpage.py @@ -8,6 +8,7 @@ # Third party imports from qtpy.QtWidgets import QGroupBox, QVBoxLayout +from spyder_kernels.utils.misc import is_module_installed # Local imports from spyder.config.base import _ @@ -16,6 +17,7 @@ class VariableExplorerConfigPage(PluginConfigPage): def setup_page(self): + # Filter Group filter_group = QGroupBox(_("Filter")) filter_data = [ ('exclude_private', _("Exclude private references")), @@ -27,20 +29,37 @@ def setup_page(self): ] filter_boxes = [self.create_checkbox(text, option) for option, text in filter_data] - - display_group = QGroupBox(_("Display")) - display_data = [('minmax', _("Show arrays min/max"), '')] - display_boxes = [self.create_checkbox(text, option, tip=tip) - for option, text, tip in display_data] - filter_layout = QVBoxLayout() for box in filter_boxes: filter_layout.addWidget(box) filter_group.setLayout(filter_layout) + # Display Group + display_group = QGroupBox(_("Display")) + display_data = [("minmax", _("Show arrays min/max"), "")] + display_boxes = [ + self.create_checkbox(text, option, tip=tip) + for option, text, tip in display_data + ] display_layout = QVBoxLayout() for box in display_boxes: display_layout.addWidget(box) + plotlibs = [ + (libname, libname) + for libname in ("matplotlib", "guiqwt") + if is_module_installed(libname) + ] + if plotlibs: + plotlib_box = self.create_combobox( + _("Plotting library:") + " ", + plotlibs, + "plotlib", + default="matplotlib", + tip=_( + "Default library used to plot data from NumPy arrays (curve, histogram, image)" + ), + ) + display_layout.addWidget(plotlib_box) display_group.setLayout(display_layout) vlayout = QVBoxLayout() From 2815e53ec3c6406f705c0422e58d72c850823a34 Mon Sep 17 00:00:00 2001 From: Pierre Raybaut Date: Thu, 17 Nov 2022 10:52:51 +0100 Subject: [PATCH 02/11] VarExp: "Plotting library" option now works for nested arrays (pop-up plotting windows) --- spyder/config/main.py | 3 +- spyder/plugins/variableexplorer/confpage.py | 18 ++-- spyder/plugins/variableexplorer/plotlib.py | 53 +++++++++++ spyder/pyplot.py | 18 ---- spyder/widgets/collectionseditor.py | 100 ++++++++++---------- 5 files changed, 113 insertions(+), 79 deletions(-) create mode 100644 spyder/plugins/variableexplorer/plotlib.py delete mode 100644 spyder/pyplot.py diff --git a/spyder/config/main.py b/spyder/config/main.py index 12eb662af22..1b20e2e0661 100644 --- a/spyder/config/main.py +++ b/spyder/config/main.py @@ -21,6 +21,7 @@ from spyder.config.appearance import APPEARANCE from spyder.plugins.editor.utils.findtasks import TASKS_PATTERN from spyder.utils.introspection.module_completion import PREFERRED_MODULES +from spyder.plugins.variableexplorer.plotlib import DEFAULT_PLOTLIB # ============================================================================= @@ -177,7 +178,7 @@ 'minmax': False, 'show_callable_attributes': True, 'show_special_attributes': False, - 'plotlib': 'matplotlib', + 'plotlib': DEFAULT_PLOTLIB, }), ('debugger', { diff --git a/spyder/plugins/variableexplorer/confpage.py b/spyder/plugins/variableexplorer/confpage.py index 1bf3df78dca..f01a9a18f11 100644 --- a/spyder/plugins/variableexplorer/confpage.py +++ b/spyder/plugins/variableexplorer/confpage.py @@ -8,11 +8,11 @@ # Third party imports from qtpy.QtWidgets import QGroupBox, QVBoxLayout -from spyder_kernels.utils.misc import is_module_installed # Local imports from spyder.config.base import _ from spyder.api.preferences import PluginConfigPage +from spyder.plugins.variableexplorer import plotlib class VariableExplorerConfigPage(PluginConfigPage): @@ -44,19 +44,17 @@ def setup_page(self): display_layout = QVBoxLayout() for box in display_boxes: display_layout.addWidget(box) - plotlibs = [ - (libname, libname) - for libname in ("matplotlib", "guiqwt") - if is_module_installed(libname) - ] - if plotlibs: + if plotlib.AVAILABLE_PLOTLIBS: plotlib_box = self.create_combobox( _("Plotting library:") + " ", - plotlibs, + zip(plotlib.AVAILABLE_PLOTLIBS, plotlib.AVAILABLE_PLOTLIBS), "plotlib", - default="matplotlib", + default=plotlib.DEFAULT_PLOTLIB, tip=_( - "Default library used to plot data from NumPy arrays (curve, histogram, image)" + "Default library used for data plotting of NumPy arrays " + "(curve, histogram, image).

Regarding the " + "%varexp magic command, this option will be " + "applied the next time a console is opened." ), ) display_layout.addWidget(plotlib_box) diff --git a/spyder/plugins/variableexplorer/plotlib.py b/spyder/plugins/variableexplorer/plotlib.py new file mode 100644 index 00000000000..14c58018a2e --- /dev/null +++ b/spyder/plugins/variableexplorer/plotlib.py @@ -0,0 +1,53 @@ +# -*- coding: utf-8 -*- +# +# Copyright © Spyder Project Contributors +# Licensed under the terms of the MIT License +# (see spyder/__init__.py for details) + +""" +Variable Explorer Plugin plotting library management. +""" + +# Standard library imports +import importlib + +# Local imports +from spyder.config.base import _ + + +# Defining compatible plotting libraries +# (default library is the first available, see `get_default_plotlib`) +LIBRARIES = ("matplotlib", "guiqwt") + + +def is_package_installed(modname): + """Check if package is installed **without importing it** + + Note: As Spyder won't start if matplotlib has been imported too early, + we do not use `utils.programs.is_module_installed` here because + it imports module to check if it's installed. + """ + return importlib.util.find_spec(modname) is not None + + +def get_available_plotlibs(): + """Return list of available plotting libraries""" + return [name for name in LIBRARIES if is_package_installed(name)] + + +def get_default_plotlib(): + """Return default plotting library""" + names = get_available_plotlibs() + if names is not None: + return names[0] + + +def get_requirement_error_message(): + """Return html error message when no library is available""" + txt = ", ".join(["%s" % name for name in LIBRARIES]) + return _("Please install a compatible plotting library (%s).") % txt + + +AVAILABLE_PLOTLIBS = get_available_plotlibs() +DEFAULT_PLOTLIB = get_default_plotlib() +REQ_ERROR_MSG = get_requirement_error_message() diff --git a/spyder/pyplot.py b/spyder/pyplot.py deleted file mode 100644 index 6890e731def..00000000000 --- a/spyder/pyplot.py +++ /dev/null @@ -1,18 +0,0 @@ -# -*- coding: utf-8 -*- -# ----------------------------------------------------------------------------- -# Copyright (c) 2009- Spyder Project Contributors -# -# Distributed under the terms of the MIT License -# (see spyder/__init__.py for details) -# ----------------------------------------------------------------------------- - - -""" -Import guiqwt's pyplot module or matplotlib's pyplot. -""" - - -try: - from guiqwt.pyplot import * -except Exception: - from matplotlib.pyplot import * diff --git a/spyder/widgets/collectionseditor.py b/spyder/widgets/collectionseditor.py index 5b8e0a76a37..8116c301e08 100644 --- a/spyder/widgets/collectionseditor.py +++ b/spyder/widgets/collectionseditor.py @@ -60,6 +60,7 @@ from spyder.plugins.variableexplorer.widgets.importwizard import ImportWizard from spyder.widgets.helperwidgets import CustomSortFilterProxy from spyder.plugins.variableexplorer.widgets.basedialog import BaseDialog +from spyder.plugins.variableexplorer.plotlib import REQ_ERROR_MSG from spyder.utils.palette import SpyderPalette from spyder.utils.stylesheet import PANES_TOOLBAR_STYLESHEET @@ -1132,57 +1133,41 @@ def view_item(self): index = index.child(index.row(), 3) self.delegate.createEditor(self, None, index, object_explorer=True) - def __prepare_plot(self): - try: - import guiqwt.pyplot #analysis:ignore - return True - except: - try: - if 'matplotlib' not in sys.modules: - import matplotlib # noqa - return True - except Exception: - QMessageBox.warning(self, _("Import error"), - _("Please install matplotlib" - " or guiqwt.")) - def plot_item(self, funcname): """Plot item""" index = self.currentIndex() - if self.__prepare_plot(): - if self.proxy_model: - key = self.source_model.get_key( - self.proxy_model.mapToSource(index)) - else: - key = self.source_model.get_key(index) - try: - self.plot(key, funcname) - except (ValueError, TypeError) as error: - QMessageBox.critical(self, _( "Plot"), - _("Unable to plot data." - "

Error message:
%s" - ) % str(error)) + if self.proxy_model: + key = self.source_model.get_key( + self.proxy_model.mapToSource(index)) + else: + key = self.source_model.get_key(index) + try: + self.plot(key, funcname) + except (ValueError, TypeError) as error: + QMessageBox.critical(self, _( "Plot"), + _("Unable to plot data." + "

Error message:
%s" + ) % str(error)) @Slot() def imshow_item(self): """Imshow item""" index = self.currentIndex() - if self.__prepare_plot(): - if self.proxy_model: - key = self.source_model.get_key( - self.proxy_model.mapToSource(index)) + if self.proxy_model: + key = self.source_model.get_key( + self.proxy_model.mapToSource(index)) + else: + key = self.source_model.get_key(index) + try: + if self.is_image(key): + self.show_image(key) else: - key = self.source_model.get_key(index) - try: - if self.is_image(key): - self.show_image(key) - else: - self.imshow(key) - except (ValueError, TypeError) as error: - QMessageBox.critical(self, _( "Plot"), - _("Unable to show image." - "

Error message:
%s" - ) % str(error)) + self.imshow(key) + except (ValueError, TypeError) as error: + QMessageBox.critical(self, _( "Plot"), + _("Unable to show image." + "

Error message:
%s" + ) % str(error)) @Slot() def save_array(self): @@ -1383,21 +1368,36 @@ def oedit(self, key): oedit) oedit(data[key]) + def __get_pyplot(self): + """Return default plotting library `pyplot` package if available. + (default plotting library is an option defined in Variable Explorer + plugin scope, e.g. "maplotlib" or "guiqwt")""" + libname = self.get_conf('plotlib', section='variable_explorer') + try: + return __import__(libname + '.pyplot', + globals(), locals(), [], 0).pyplot + except ImportError: + # This should not happen, unless plotting library has been + # uninstalled after option has been set in Variable Explorer + QMessageBox.critical(self, _("Import error"), REQ_ERROR_MSG) + def plot(self, key, funcname): """Plot item""" data = self.source_model.get_data() - import spyder.pyplot as plt - plt.figure() - getattr(plt, funcname)(data[key]) - plt.show() + plt = self.__get_pyplot() + if plt is not None: + plt.figure() + getattr(plt, funcname)(data[key]) + plt.show() def imshow(self, key): """Show item's image""" data = self.source_model.get_data() - import spyder.pyplot as plt - plt.figure() - plt.imshow(data[key]) - plt.show() + plt = self.__get_pyplot() + if plt is not None: + plt.figure() + plt.imshow(data[key]) + plt.show() def show_image(self, key): """Show image (item is a PIL image)""" From 9a4fb33234b88bd0c004309ecb1b11357a718f08 Mon Sep 17 00:00:00 2001 From: Pierre Raybaut Date: Thu, 24 Nov 2022 11:44:37 +0100 Subject: [PATCH 03/11] figurebrowser.FigureThumbnail: removed unused signals --- spyder/plugins/plots/widgets/figurebrowser.py | 29 ------------------- 1 file changed, 29 deletions(-) diff --git a/spyder/plugins/plots/widgets/figurebrowser.py b/spyder/plugins/plots/widgets/figurebrowser.py index c619feb193e..74bda0626e2 100644 --- a/spyder/plugins/plots/widgets/figurebrowser.py +++ b/spyder/plugins/plots/widgets/figurebrowser.py @@ -772,8 +772,6 @@ def add_thumbnail(self, fig, fmt): parent=self, background_color=self.background_color) thumbnail.canvas.load_figure(fig, fmt) thumbnail.sig_canvas_clicked.connect(self.set_current_thumbnail) - thumbnail.sig_remove_figure_requested.connect(self.remove_thumbnail) - thumbnail.sig_save_figure_requested.connect(self.save_figure_as) thumbnail.sig_context_menu_requested.connect( lambda point: self.show_context_menu(point, thumbnail)) self._thumbnails.append(thumbnail) @@ -796,8 +794,6 @@ def remove_all_thumbnails(self): """Remove all thumbnails.""" for thumbnail in self._thumbnails: thumbnail.sig_canvas_clicked.disconnect() - thumbnail.sig_remove_figure_requested.disconnect() - thumbnail.sig_save_figure_requested.disconnect() self.layout().removeWidget(thumbnail) thumbnail.setParent(None) thumbnail.hide() @@ -815,8 +811,6 @@ def remove_thumbnail(self, thumbnail): # Disconnect signals try: thumbnail.sig_canvas_clicked.disconnect() - thumbnail.sig_remove_figure_requested.disconnect() - thumbnail.sig_save_figure_requested.disconnect() except TypeError: pass @@ -945,29 +939,6 @@ class FigureThumbnail(QWidget): The clicked figure thumbnail. """ - sig_remove_figure_requested = Signal(object) - """ - This signal is emitted to request the removal of a figure thumbnail. - - Parameters - ---------- - figure_thumbnail: spyder.plugins.plots.widget.figurebrowser.FigureThumbnail - The figure thumbnail to remove. - """ - - sig_save_figure_requested = Signal(object, str) - """ - This signal is emitted to request the saving of a figure thumbnail. - - Parameters - ---------- - figure_thumbnail: spyder.plugins.plots.widget.figurebrowser.FigureThumbnail - The figure thumbnail to save. - format: str - The image format to use when saving the image. One of "image/png", - "image/jpeg" and "image/svg+xml". - """ - sig_context_menu_requested = Signal(QPoint) """ This signal is emitted to request a context menu. From 77cd9093e45594bc315dc8138132cbf7ceeee60f Mon Sep 17 00:00:00 2001 From: Pierre Raybaut Date: Fri, 25 Nov 2022 09:10:40 +0100 Subject: [PATCH 04/11] Update spyder/widgets/collectionseditor.py Improve formatting and message a bit Co-authored-by: Carlos Cordoba --- spyder/widgets/collectionseditor.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/spyder/widgets/collectionseditor.py b/spyder/widgets/collectionseditor.py index 8116c301e08..5805a6d3328 100644 --- a/spyder/widgets/collectionseditor.py +++ b/spyder/widgets/collectionseditor.py @@ -1144,10 +1144,12 @@ def plot_item(self, funcname): try: self.plot(key, funcname) except (ValueError, TypeError) as error: - QMessageBox.critical(self, _( "Plot"), - _("Unable to plot data." - "

Error message:
%s" - ) % str(error)) + QMessageBox.critical( + self, + _( "Plot"), + _("Unable to plot data.

" + "The error message was:
%s") % str(error) + ) @Slot() def imshow_item(self): From cc04605822151be428f6eb247d8908b9f1d8ec16 Mon Sep 17 00:00:00 2001 From: Pierre Raybaut Date: Fri, 25 Nov 2022 09:14:30 +0100 Subject: [PATCH 05/11] Update spyder/widgets/collectionseditor.py Improve formatting and message a bit Co-authored-by: Carlos Cordoba --- spyder/widgets/collectionseditor.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/spyder/widgets/collectionseditor.py b/spyder/widgets/collectionseditor.py index 5805a6d3328..1235a25e8da 100644 --- a/spyder/widgets/collectionseditor.py +++ b/spyder/widgets/collectionseditor.py @@ -1166,10 +1166,12 @@ def imshow_item(self): else: self.imshow(key) except (ValueError, TypeError) as error: - QMessageBox.critical(self, _( "Plot"), - _("Unable to show image." - "

Error message:
%s" - ) % str(error)) + QMessageBox.critical( + self, + _( "Plot"), + _("Unable to show image.

" + "The error message was:
%s") % str(error) + ) @Slot() def save_array(self): From 931f69881512cd7691b3568384ffd915ea0abfa0 Mon Sep 17 00:00:00 2001 From: Pierre Raybaut Date: Fri, 25 Nov 2022 09:14:54 +0100 Subject: [PATCH 06/11] Update spyder/widgets/collectionseditor.py Improve formatting and message a bit Co-authored-by: Carlos Cordoba --- spyder/widgets/collectionseditor.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/spyder/widgets/collectionseditor.py b/spyder/widgets/collectionseditor.py index 1235a25e8da..cd91a724f9e 100644 --- a/spyder/widgets/collectionseditor.py +++ b/spyder/widgets/collectionseditor.py @@ -1373,9 +1373,12 @@ def oedit(self, key): oedit(data[key]) def __get_pyplot(self): - """Return default plotting library `pyplot` package if available. - (default plotting library is an option defined in Variable Explorer - plugin scope, e.g. "maplotlib" or "guiqwt")""" + """ + Return default plotting library `pyplot` package if available. + + The default plotting library is an option defined in the Variable + Explorer plugin scope, e.g. "maplotlib" or "guiqwt". + """ libname = self.get_conf('plotlib', section='variable_explorer') try: return __import__(libname + '.pyplot', From c2f51cee902c9f2b72ac5faee8344f5f40f0010e Mon Sep 17 00:00:00 2001 From: Pierre Raybaut Date: Fri, 25 Nov 2022 09:15:39 +0100 Subject: [PATCH 07/11] Update spyder/widgets/collectionseditor.py Co-authored-by: Carlos Cordoba --- spyder/widgets/collectionseditor.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spyder/widgets/collectionseditor.py b/spyder/widgets/collectionseditor.py index cd91a724f9e..aed579d9ffe 100644 --- a/spyder/widgets/collectionseditor.py +++ b/spyder/widgets/collectionseditor.py @@ -1383,7 +1383,7 @@ def __get_pyplot(self): try: return __import__(libname + '.pyplot', globals(), locals(), [], 0).pyplot - except ImportError: + except Exception: # This should not happen, unless plotting library has been # uninstalled after option has been set in Variable Explorer QMessageBox.critical(self, _("Import error"), REQ_ERROR_MSG) From 7adeeceea33c88919440c4f2852c5fe1f72811d5 Mon Sep 17 00:00:00 2001 From: Pierre Raybaut Date: Fri, 25 Nov 2022 10:32:07 +0100 Subject: [PATCH 08/11] VarExp "Plotting library" option: now using static choice list (see PR #20075) --- spyder/plugins/variableexplorer/confpage.py | 11 +++++++---- spyder/plugins/variableexplorer/plotlib.py | 6 +++--- spyder/widgets/collectionseditor.py | 5 ++--- 3 files changed, 12 insertions(+), 10 deletions(-) diff --git a/spyder/plugins/variableexplorer/confpage.py b/spyder/plugins/variableexplorer/confpage.py index f01a9a18f11..12b52ade17a 100644 --- a/spyder/plugins/variableexplorer/confpage.py +++ b/spyder/plugins/variableexplorer/confpage.py @@ -7,7 +7,7 @@ """Variable Explorer Plugin Configuration Page.""" # Third party imports -from qtpy.QtWidgets import QGroupBox, QVBoxLayout +from qtpy.QtWidgets import QGroupBox, QVBoxLayout, QLabel # Local imports from spyder.config.base import _ @@ -45,9 +45,9 @@ def setup_page(self): for box in display_boxes: display_layout.addWidget(box) if plotlib.AVAILABLE_PLOTLIBS: - plotlib_box = self.create_combobox( + plotlib_opt = self.create_combobox( _("Plotting library:") + " ", - zip(plotlib.AVAILABLE_PLOTLIBS, plotlib.AVAILABLE_PLOTLIBS), + zip(plotlib.SUPPORTED_PLOTLIBS, plotlib.SUPPORTED_PLOTLIBS), "plotlib", default=plotlib.DEFAULT_PLOTLIB, tip=_( @@ -57,7 +57,10 @@ def setup_page(self): "applied the next time a console is opened." ), ) - display_layout.addWidget(plotlib_box) + else: + plotlib_opt = QLabel("%s" % plotlib.REQ_ERROR_MSG) + plotlib_opt.setWordWrap(True) + display_layout.addWidget(plotlib_opt) display_group.setLayout(display_layout) vlayout = QVBoxLayout() diff --git a/spyder/plugins/variableexplorer/plotlib.py b/spyder/plugins/variableexplorer/plotlib.py index 14c58018a2e..996f79f4daf 100644 --- a/spyder/plugins/variableexplorer/plotlib.py +++ b/spyder/plugins/variableexplorer/plotlib.py @@ -17,7 +17,7 @@ # Defining compatible plotting libraries # (default library is the first available, see `get_default_plotlib`) -LIBRARIES = ("matplotlib", "guiqwt") +SUPPORTED_PLOTLIBS = ("matplotlib", "guiqwt") def is_package_installed(modname): @@ -32,7 +32,7 @@ def is_package_installed(modname): def get_available_plotlibs(): """Return list of available plotting libraries""" - return [name for name in LIBRARIES if is_package_installed(name)] + return [name for name in SUPPORTED_PLOTLIBS if is_package_installed(name)] def get_default_plotlib(): @@ -44,7 +44,7 @@ def get_default_plotlib(): def get_requirement_error_message(): """Return html error message when no library is available""" - txt = ", ".join(["%s" % name for name in LIBRARIES]) + txt = ", ".join(["%s" % name for name in SUPPORTED_PLOTLIBS]) return _("Please install a compatible plotting library (%s).") % txt diff --git a/spyder/widgets/collectionseditor.py b/spyder/widgets/collectionseditor.py index aed579d9ffe..4fd06d4c9f7 100644 --- a/spyder/widgets/collectionseditor.py +++ b/spyder/widgets/collectionseditor.py @@ -1384,9 +1384,8 @@ def __get_pyplot(self): return __import__(libname + '.pyplot', globals(), locals(), [], 0).pyplot except Exception: - # This should not happen, unless plotting library has been - # uninstalled after option has been set in Variable Explorer - QMessageBox.critical(self, _("Import error"), REQ_ERROR_MSG) + msg = _("Unable to plot data using %s library.") % libname + QMessageBox.critical(self, _("Error"), msg + "

" + REQ_ERROR_MSG) def plot(self, key, funcname): """Plot item""" From 2b75726b538443ca0140cfa6ed02ab75c5bb8318 Mon Sep 17 00:00:00 2001 From: Pierre Raybaut Date: Fri, 25 Nov 2022 11:09:24 +0100 Subject: [PATCH 09/11] %varexp: now handling exceptions --- .../spyder_kernels/console/start.py | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/external-deps/spyder-kernels/spyder_kernels/console/start.py b/external-deps/spyder-kernels/spyder_kernels/console/start.py index 4b597fb06b7..0dae58077ff 100644 --- a/external-deps/spyder-kernels/spyder_kernels/console/start.py +++ b/external-deps/spyder-kernels/spyder_kernels/console/start.py @@ -254,11 +254,21 @@ def varexp(line): """ ip = get_ipython() #analysis:ignore funcname, name = line.split() + try: + data = ip.kernel._get_current_namespace()[name] + except KeyError: + print("\033[41mVariable %s does not exist in current namespace.\033[0m" % name) plotlib = os.environ.get('SPY_VAREXP_PLOTLIB') - pyplot = __import__(plotlib + '.pyplot', globals(), locals(), [], 0).pyplot - pyplot.figure() - getattr(pyplot, funcname[2:])(ip.kernel._get_current_namespace()[name]) - pyplot.show() + try: + pyplot = __import__(plotlib + '.pyplot', globals(), locals(), [], 0).pyplot + pyplot.figure() + getattr(pyplot, funcname[2:])(data) + pyplot.show() + except (ImportError, ModuleNotFoundError): + print("\033[41mPlease install %s or select an available plotting library " + "in Variable Explorer preferences.\033[0m" % plotlib) + except Exception: + ip.showtraceback(sys.exc_info()) def main(): From a293df44d983f315c912cbfbe6838cdaa90a0b12 Mon Sep 17 00:00:00 2001 From: Pierre Raybaut Date: Sat, 26 Nov 2022 11:34:36 +0100 Subject: [PATCH 10/11] git subrepo pull --remote=https://github.com/PierreRaybaut/spyder-kernels.git --branch=improving_guiqwt_integration_kernel --update --force external-deps/spyder-kernels subrepo: subdir: "external-deps/spyder-kernels" merged: "6c5418b28" upstream: origin: "https://github.com/PierreRaybaut/spyder-kernels.git" branch: "improving_guiqwt_integration_kernel" commit: "6c5418b28" git-subrepo: version: "0.4.5" origin: "???" commit: "???" --- external-deps/spyder-kernels/.gitrepo | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/external-deps/spyder-kernels/.gitrepo b/external-deps/spyder-kernels/.gitrepo index 8289cc5780e..6211a979227 100644 --- a/external-deps/spyder-kernels/.gitrepo +++ b/external-deps/spyder-kernels/.gitrepo @@ -4,9 +4,9 @@ ; git-subrepo command. See https://github.com/git-commands/git-subrepo#readme ; [subrepo] - remote = https://github.com/spyder-ide/spyder-kernels.git - branch = master - commit = 34a6d634383f27c61cd92172be94c1b344bc5cf1 - parent = f80c897ac6aafb65c421da50a63e2092f32ff7e2 + remote = https://github.com/PierreRaybaut/spyder-kernels.git + branch = improving_guiqwt_integration_kernel + commit = 6c5418b2866bcd558bd9c4227ff7584b601bed28 + parent = 2b75726b538443ca0140cfa6ed02ab75c5bb8318 method = merge cmdver = 0.4.5 From dc8001dc76636717c5b7f7bf52149986fc220ecd Mon Sep 17 00:00:00 2001 From: Pierre Raybaut Date: Sat, 26 Nov 2022 12:16:47 +0100 Subject: [PATCH 11/11] VarExp: fixed default plotlib issues --- spyder/plugins/variableexplorer/confpage.py | 34 +++++++++++---------- spyder/plugins/variableexplorer/plotlib.py | 12 ++------ 2 files changed, 21 insertions(+), 25 deletions(-) diff --git a/spyder/plugins/variableexplorer/confpage.py b/spyder/plugins/variableexplorer/confpage.py index 12b52ade17a..e40c2eefd2b 100644 --- a/spyder/plugins/variableexplorer/confpage.py +++ b/spyder/plugins/variableexplorer/confpage.py @@ -44,23 +44,25 @@ def setup_page(self): display_layout = QVBoxLayout() for box in display_boxes: display_layout.addWidget(box) - if plotlib.AVAILABLE_PLOTLIBS: - plotlib_opt = self.create_combobox( - _("Plotting library:") + " ", - zip(plotlib.SUPPORTED_PLOTLIBS, plotlib.SUPPORTED_PLOTLIBS), - "plotlib", - default=plotlib.DEFAULT_PLOTLIB, - tip=_( - "Default library used for data plotting of NumPy arrays " - "(curve, histogram, image).

Regarding the " - "%varexp magic command, this option will be " - "applied the next time a console is opened." - ), - ) - else: - plotlib_opt = QLabel("%s" % plotlib.REQ_ERROR_MSG) - plotlib_opt.setWordWrap(True) + plotlib_opt = self.create_combobox( + _("Plotting library:") + " ", + zip(plotlib.SUPPORTED_PLOTLIBS, plotlib.SUPPORTED_PLOTLIBS), + "plotlib", + default=plotlib.DEFAULT_PLOTLIB, + tip=_( + "Default library used for data plotting of NumPy arrays " + "(curve, histogram, image).

Regarding the " + "%varexp magic command, this option will be " + "applied the next time a console is opened." + ), + ) display_layout.addWidget(plotlib_opt) + if not plotlib.AVAILABLE_PLOTLIBS: + msg = "%s" % plotlib.REQ_ERROR_MSG[:-1] + msg += " " + _("for enabling data plotting from Spyder IDE process.") + plotlib_msg = QLabel(msg) + plotlib_msg.setWordWrap(True) + display_layout.addWidget(plotlib_msg) display_group.setLayout(display_layout) vlayout = QVBoxLayout() diff --git a/spyder/plugins/variableexplorer/plotlib.py b/spyder/plugins/variableexplorer/plotlib.py index 996f79f4daf..1274ffcab76 100644 --- a/spyder/plugins/variableexplorer/plotlib.py +++ b/spyder/plugins/variableexplorer/plotlib.py @@ -16,9 +16,11 @@ # Defining compatible plotting libraries -# (default library is the first available, see `get_default_plotlib`) SUPPORTED_PLOTLIBS = ("matplotlib", "guiqwt") +# Default library is the first one of the list +DEFAULT_PLOTLIB = SUPPORTED_PLOTLIBS[0] + def is_package_installed(modname): """Check if package is installed **without importing it** @@ -35,13 +37,6 @@ def get_available_plotlibs(): return [name for name in SUPPORTED_PLOTLIBS if is_package_installed(name)] -def get_default_plotlib(): - """Return default plotting library""" - names = get_available_plotlibs() - if names is not None: - return names[0] - - def get_requirement_error_message(): """Return html error message when no library is available""" txt = ", ".join(["%s" % name for name in SUPPORTED_PLOTLIBS]) @@ -49,5 +44,4 @@ def get_requirement_error_message(): AVAILABLE_PLOTLIBS = get_available_plotlibs() -DEFAULT_PLOTLIB = get_default_plotlib() REQ_ERROR_MSG = get_requirement_error_message()