diff --git a/docs/source/jupyter.rst b/docs/source/jupyter.rst new file mode 100644 index 00000000..9c48301e --- /dev/null +++ b/docs/source/jupyter.rst @@ -0,0 +1,51 @@ +####################### + Jupyter notebook usage +####################### + +IDStools command-line tools can also be used directly from Jupyter notebooks. +Import ``idstools`` once in the notebook kernel to register the IDStools +commands as IPython line magics: + +.. code-block:: python + + import idstools + +After that, commands such as ``idsprint`` and ``plotequilibrium`` can be +called with ``%``: + +.. code-block:: python + + %idsprint -u "imas:hdf5?path=/work/imas/shared/imasdb/ITER/3/134174/117#core_profiles/profiles_1d[0]/electrons/temperature" -p + +.. code-block:: python + + %plotequilibrium -u "imas:hdf5?path=/work/imas/shared/imasdb/ITER/3/100507/5" + + +****************************** + Interactive Matplotlib plots +****************************** + +For interactive Matplotlib figures in Jupyter, use the ``ipympl`` backend. If +``ipympl`` is installed in the same Python environment as IDStools, the backend +can be selected with Matplotlib's notebook magic before plotting: + +.. code-block:: python + + %matplotlib widget + import idstools + + %idsprint -u "imas:hdf5?path=/work/imas/shared/imasdb/ITER/3/134174/117#core_profiles/profiles_1d[0]/electrons/temperature" -p + +Alternatively, the backend can be selected through the IDStools ``--rc`` +option before Matplotlib has been imported in the current kernel: + +.. code-block:: python + + import idstools + + %idsprint -u "imas:hdf5?path=/work/imas/shared/imasdb/ITER/3/134174/117#core_profiles/profiles_1d[0]/electrons/temperature" -p --rc "backend='module://ipympl.backend_nbagg'" + +.. code-block:: python + + %plotequilibrium -u "imas:hdf5?path=/work/imas/shared/imasdb/ITER/3/100507/5" --rc "backend='module://ipympl.backend_nbagg'" diff --git a/docs/source/tools.rst b/docs/source/tools.rst index a90cc39d..1b5a1685 100644 --- a/docs/source/tools.rst +++ b/docs/source/tools.rst @@ -12,3 +12,4 @@ Following are the different command line tools available in the ids_manipulation_tools database_tools scenariodb_tools + jupyter diff --git a/idstools/compute/equilibrium.py b/idstools/compute/equilibrium.py index 941a25c8..5f84d4cb 100644 --- a/idstools/compute/equilibrium.py +++ b/idstools/compute/equilibrium.py @@ -1384,7 +1384,7 @@ def get_profiles_1d_quantities(self, time_slice, attributes=None): if ids_field.has_value: quantities[attribute] = eval(f"self.ids.time_slice[{time_slice}].profiles_1d.{attribute}") else: - logger.error(f"self.ids.time_slice[{time_slice}].profiles_1d.{attribute} not found") + logger.warning(f"self.ids.time_slice[{time_slice}].profiles_1d.{attribute} not found") return quantities def get_global_quantities(self, time_slice=None, attributes=None): diff --git a/idstools/scripts/bin/dblist b/idstools/scripts/bin/dblist index 57d9e72a..d30a9e75 100644 --- a/idstools/scripts/bin/dblist +++ b/idstools/scripts/bin/dblist @@ -234,7 +234,15 @@ def print_times(dbs, args, print_times=False, pulse_number=None, run_number=None print(TAB * 4 + " Run: " + extended(str(run), RUN_STR_LEN)) available_ids_and_times = get_available_ids_and_times(connection) for idsname, times in available_ids_and_times: - if len(times) == 1 and np.isnan(times[0]): + if times is None: + print( + TAB * 5 + + extended(idsname, IDSNAME_STR_LEN) + + ": " + + extended("N/A", SLICENUM_STR_LEN) + + " slices ( time unavailable )" + ) + elif len(times) == 1 and np.isnan(times[0]): print( TAB * 5 + extended(idsname, IDSNAME_STR_LEN) @@ -311,7 +319,15 @@ def print_times_with_folder(dbs, print_times=False, pulse_number=None, run_numbe continue available_ids_and_times = get_available_ids_and_times(connection) for idsname, times in available_ids_and_times: - if times is not None: + if times is None: + print( + TAB * 15 + + extended(idsname, IDSNAME_STR_LEN) + + ": " + + extended("N/A", SLICENUM_STR_LEN) + + " slices ( time unavailable )" + ) + else: if len(times) == 1 and np.isnan(times[0]): print( TAB * 15 diff --git a/idstools/scripts/bin/idsdiff b/idstools/scripts/bin/idsdiff index a7cb192b..6cec2dcb 100644 --- a/idstools/scripts/bin/idsdiff +++ b/idstools/scripts/bin/idsdiff @@ -10,6 +10,7 @@ import sys import time from io import BytesIO +from idstools.view.common import PlotCanvas import matplotlib.pyplot as plt import numpy as np import rich @@ -43,8 +44,6 @@ slicing_methods = { def view_plot(ax, field, coordinate, field_name="", coordinate_name="", field_unit="", coordinate_unit="", **kwargs): - from idstools.view.common import PlotCanvas - if not isinstance(field, (imas.ids_primitive.IDSNumericArray, np.ndarray)): print("Not a numeric array, Please select ids path") return @@ -506,7 +505,10 @@ if __name__ == "__main__": ) parser.add_argument( "--save", - help="Save figure at default location", + help=( + "Save the plot to a file instead of opening a window. " + "If no plot window is available, it is saved automatically." + ), action="store_true", ) parser.add_argument( @@ -702,8 +704,6 @@ if __name__ == "__main__": ] = compare_data if args.plot and not args.html: - from idstools.view.common import PlotCanvas - canvas = PlotCanvas(1, 1) canvas.update_style(args.rc) ax = canvas.add_axes(title="", xlabel="", row=0, col=0) diff --git a/idstools/scripts/bin/idslist b/idstools/scripts/bin/idslist index 63578322..c5cf7456 100644 --- a/idstools/scripts/bin/idslist +++ b/idstools/scripts/bin/idslist @@ -184,8 +184,8 @@ if __name__ == "__main__": type = time_array elif time_array is None: value = f"{not_applicable}" - type = "unknown" - table.add_row(ids_name, value, Pretty(type)) + type = not_applicable + table.add_row(ids_name, value, type if isinstance(type, str) else Pretty(type)) if args.fullarray is True: with np.printoptions(threshold=sys.maxsize, linewidth=1024, precision=4): console.print(table) diff --git a/idstools/scripts/bin/idsprint b/idstools/scripts/bin/idsprint index a0f65aef..e85df44e 100644 --- a/idstools/scripts/bin/idsprint +++ b/idstools/scripts/bin/idsprint @@ -522,7 +522,10 @@ if __name__ == "__main__": ) parser.add_argument( "--save", - help="Save figure at default location", + help=( + "Save the plot to a file instead of opening a window. " + "If no plot window is available, it is saved automatically." + ), action="store_true", ) parser.add_argument( @@ -559,7 +562,10 @@ if __name__ == "__main__": table.add_column("SLICES", style="green") table.add_column("TIME", style="green") for ids_name, time_array in available_ids_and_times: - if len(time_array) == 1 and np.isnan(time_array[0]): + if time_array is None: + value = "N/A" + type = "unknown" + elif len(time_array) == 1 and np.isnan(time_array[0]): value = f"{question_string}" type = "heterogeneous IDS" elif len(time_array) == 1 and time_array[0] == -np.inf: diff --git a/idstools/scripts/bin/plotcoresources b/idstools/scripts/bin/plotcoresources index 6e7f268c..6388fc40 100644 --- a/idstools/scripts/bin/plotcoresources +++ b/idstools/scripts/bin/plotcoresources @@ -35,7 +35,10 @@ if __name__ == "__main__": parser.add_argument("-t", "--time", help="time", required=False, type=float, default=-99.0) parser.add_argument( "--save", - help="Save figure at default location", + help=( + "Save the plot to a file instead of opening a window. " + "If no plot window is available, it is saved automatically." + ), action="store_true", ) parser.add_argument( diff --git a/idstools/scripts/bin/plotcoretransport b/idstools/scripts/bin/plotcoretransport index 7eb2c9da..da0a4c96 100644 --- a/idstools/scripts/bin/plotcoretransport +++ b/idstools/scripts/bin/plotcoretransport @@ -64,7 +64,10 @@ if __name__ == "__main__": ) parser.add_argument( "--save", - help="Save figure at default location", + help=( + "Save the plot to a file instead of opening a window. " + "If no plot window is available, it is saved automatically." + ), action="store_true", ) parser.add_argument( diff --git a/idstools/scripts/bin/ploteccomposition b/idstools/scripts/bin/ploteccomposition index 8b94d9f8..adf983df 100644 --- a/idstools/scripts/bin/ploteccomposition +++ b/idstools/scripts/bin/ploteccomposition @@ -52,7 +52,10 @@ if __name__ == "__main__": ) parser.add_argument( "--save", - help="Save figure at default location", + help=( + "Save the plot to a file instead of opening a window. " + "If no plot window is available, it is saved automatically." + ), action="store_true", ) parser.add_argument( diff --git a/idstools/scripts/bin/plotecray b/idstools/scripts/bin/plotecray index b9968b75..c858fa90 100644 --- a/idstools/scripts/bin/plotecray +++ b/idstools/scripts/bin/plotecray @@ -68,7 +68,10 @@ if __name__ == "__main__": parser.add_argument( "--save", - help="Save figure at default location", + help=( + "Save the plot to a file instead of opening a window. " + "If no plot window is available, it is saved automatically." + ), action="store_true", ) parser.add_argument( diff --git a/idstools/scripts/bin/plotecstrayradiation b/idstools/scripts/bin/plotecstrayradiation index 8d734f26..7604bfd3 100644 --- a/idstools/scripts/bin/plotecstrayradiation +++ b/idstools/scripts/bin/plotecstrayradiation @@ -56,7 +56,10 @@ if __name__ == "__main__": ) parser.add_argument( "--save", - help="Save figure at default location", + help=( + "Save the plot to a file instead of opening a window. " + "If no plot window is available, it is saved automatically." + ), action="store_true", ) parser.add_argument( diff --git a/idstools/scripts/bin/plotedgeprofiles b/idstools/scripts/bin/plotedgeprofiles index 76656958..f554f9de 100644 --- a/idstools/scripts/bin/plotedgeprofiles +++ b/idstools/scripts/bin/plotedgeprofiles @@ -59,7 +59,10 @@ if __name__ == "__main__": ) parser.add_argument( "--save", - help="Save figure at default location", + help=( + "Save the plot to a file instead of opening a window. " + "If no plot window is available, it is saved automatically." + ), action="store_true", ) parser.add_argument( diff --git a/idstools/scripts/bin/plotequicomp b/idstools/scripts/bin/plotequicomp index 19f5a53d..cea4782d 100644 --- a/idstools/scripts/bin/plotequicomp +++ b/idstools/scripts/bin/plotequicomp @@ -10,6 +10,9 @@ try: except ImportError: import imas import numpy as np + +# PlotCanvas configures a --rc backend before Matplotlib is imported. +from idstools.view.common import PROVENANCE_TITLE_STYLE, PlotCanvas from matplotlib.animation import FuncAnimation from matplotlib.widgets import Slider from rich_argparse import RichHelpFormatter @@ -20,7 +23,6 @@ from idstools.utils.clihelper import ( rcparam_parser, ) from idstools.utils.idslogger import setup_logger -from idstools.view.common import PROVENANCE_TITLE_STYLE, PlotCanvas from idstools.view.equilibrium import EquilibriumView from idstools.view.wall import WallView @@ -58,7 +60,10 @@ if __name__ == "__main__": parser.add_argument( "--save", - help="Save figure at default location", + help=( + "Save the plot to a file instead of opening a window. " + "If no plot window is available, it is saved automatically." + ), action="store_true", ) parser.add_argument( diff --git a/idstools/scripts/bin/plotequilibrium b/idstools/scripts/bin/plotequilibrium index d6053d4f..1da27c78 100644 --- a/idstools/scripts/bin/plotequilibrium +++ b/idstools/scripts/bin/plotequilibrium @@ -12,6 +12,8 @@ try: import imaspy as imas except ImportError: import imas +# PlotCanvas configures a --rc backend before mpl_toolkits imports Matplotlib. +from idstools.view.common import PlotCanvas from mpl_toolkits.axes_grid1.inset_locator import inset_axes from rich_argparse import RichHelpFormatter @@ -27,7 +29,6 @@ from idstools.utils.clihelper import ( rcparam_parser, ) from idstools.utils.idslogger import setup_logger -from idstools.view.common import PlotCanvas from idstools.view.domain.mdplot import plot_machine_description from idstools.view.equilibrium import EquilibriumView @@ -86,7 +87,10 @@ if __name__ == "__main__": ) parser.add_argument( "--save", - help="Save figure at default location", + help=( + "Save the plot to a file instead of opening a window. " + "If no plot window is available, it is saved automatically." + ), action="store_true", ) parser.add_argument( diff --git a/idstools/scripts/bin/plothcd b/idstools/scripts/bin/plothcd index 8b90ec57..d9001d3f 100644 --- a/idstools/scripts/bin/plothcd +++ b/idstools/scripts/bin/plothcd @@ -272,7 +272,10 @@ if __name__ == "__main__": ) parser.add_argument( "--save", - help="Save figure at default location", + help=( + "Save the plot to a file instead of opening a window. " + "If no plot window is available, it is saved automatically." + ), action="store_true", ) parser.add_argument( diff --git a/idstools/scripts/bin/plothcddistributions b/idstools/scripts/bin/plothcddistributions index 5ceeeaa6..a03d0c2e 100644 --- a/idstools/scripts/bin/plothcddistributions +++ b/idstools/scripts/bin/plothcddistributions @@ -92,7 +92,10 @@ if __name__ == "__main__": parser.add_argument( "--save", - help="Save figure at default location", + help=( + "Save the plot to a file instead of opening a window. " + "If no plot window is available, it is saved automatically." + ), action="store_true", ) parser.add_argument( diff --git a/idstools/scripts/bin/plothcdwaves b/idstools/scripts/bin/plothcdwaves index 5eddd9e3..462123e4 100644 --- a/idstools/scripts/bin/plothcdwaves +++ b/idstools/scripts/bin/plothcdwaves @@ -127,7 +127,10 @@ if __name__ == "__main__": parser.add_argument("-l", "--hide_legend", help="remove the legend from graphs", action="store_true") parser.add_argument( "--save", - help="Save figure at default location", + help=( + "Save the plot to a file instead of opening a window. " + "If no plot window is available, it is saved automatically." + ), action="store_true", ) parser.add_argument( diff --git a/idstools/scripts/bin/plotkineticprofiles b/idstools/scripts/bin/plotkineticprofiles index 1016cf72..078cae1b 100644 --- a/idstools/scripts/bin/plotkineticprofiles +++ b/idstools/scripts/bin/plotkineticprofiles @@ -36,7 +36,10 @@ if __name__ == "__main__": ) parser.add_argument( "--save", - help="Save figure at default location", + help=( + "Save the plot to a file instead of opening a window. " + "If no plot window is available, it is saved automatically." + ), action="store_true", ) parser.add_argument( diff --git a/idstools/scripts/bin/plotmachinedescription b/idstools/scripts/bin/plotmachinedescription index bf661565..cad8e9eb 100644 --- a/idstools/scripts/bin/plotmachinedescription +++ b/idstools/scripts/bin/plotmachinedescription @@ -52,7 +52,10 @@ if __name__ == "__main__": ) parser.add_argument( "--save", - help="Save figure at default location", + help=( + "Save the plot to a file instead of opening a window. " + "If no plot window is available, it is saved automatically." + ), action="store_true", ) parser.add_argument( diff --git a/idstools/scripts/bin/plotneutron b/idstools/scripts/bin/plotneutron index ec26631f..efc29de4 100644 --- a/idstools/scripts/bin/plotneutron +++ b/idstools/scripts/bin/plotneutron @@ -38,7 +38,10 @@ if __name__ == "__main__": parser.add_argument("-t", "--time", help="Time", required=False, type=float, default=-99.0) parser.add_argument( "--save", - help="Save figure at default location", + help=( + "Save the plot to a file instead of opening a window. " + "If no plot window is available, it is saved automatically." + ), action="store_true", ) parser.add_argument( diff --git a/idstools/scripts/bin/plotpressure b/idstools/scripts/bin/plotpressure index 2ab2f476..6751a4ef 100644 --- a/idstools/scripts/bin/plotpressure +++ b/idstools/scripts/bin/plotpressure @@ -40,7 +40,10 @@ if __name__ == "__main__": parser.add_argument("-t", "--time", type=float, help="Time", default=-99) parser.add_argument( "--save", - help="Save figure at default location", + help=( + "Save the plot to a file instead of opening a window. " + "If no plot window is available, it is saved automatically." + ), action="store_true", ) parser.add_argument( diff --git a/idstools/scripts/bin/plotrotation b/idstools/scripts/bin/plotrotation index 819d83ff..43fcbd46 100644 --- a/idstools/scripts/bin/plotrotation +++ b/idstools/scripts/bin/plotrotation @@ -36,7 +36,10 @@ if __name__ == "__main__": parser.add_argument("-t", "--time", help="Time", required=False, type=float, default=99.0) parser.add_argument( "--save", - help="Save figure at default location", + help=( + "Save the plot to a file instead of opening a window. " + "If no plot window is available, it is saved automatically." + ), action="store_true", ) parser.add_argument( diff --git a/idstools/scripts/bin/plotscenario b/idstools/scripts/bin/plotscenario index 87ce34a3..1b2a2ad2 100644 --- a/idstools/scripts/bin/plotscenario +++ b/idstools/scripts/bin/plotscenario @@ -54,7 +54,10 @@ if __name__ == "__main__": ) parser.add_argument( "--save", - help="Save figure at default location", + help=( + "Save the plot to a file instead of opening a window. " + "If no plot window is available, it is saved automatically." + ), action="store_true", ) parser.add_argument( diff --git a/idstools/scripts/bin/plotspectrometry b/idstools/scripts/bin/plotspectrometry index d5d86d4b..45ff434e 100644 --- a/idstools/scripts/bin/plotspectrometry +++ b/idstools/scripts/bin/plotspectrometry @@ -42,7 +42,10 @@ if __name__ == "__main__": ) parser.add_argument( "--save", - help="Save figure at default location", + help=( + "Save the plot to a file instead of opening a window. " + "If no plot window is available, it is saved automatically." + ), action="store_true", ) parser.add_argument( diff --git a/idstools/utils/clihelper.py b/idstools/utils/clihelper.py index d5c9a438..9df05215 100644 --- a/idstools/utils/clihelper.py +++ b/idstools/utils/clihelper.py @@ -1,6 +1,17 @@ import argparse +import logging import os import re +from datetime import datetime + +from idstools.utils.matplotlib_backend import ( + _configure_backend_from_cli_rc, + _is_jupyter, +) + +_configure_backend_from_cli_rc() + +logger = logging.getLogger("module") try: import imaspy as imas @@ -196,38 +207,45 @@ def get_title(imasargs, title="", time_value=None): def get_file_name(imasargs, title="", time_value=None): - _file_name = "" - if title: - _file_name += f"{title}_" - if "uri" in imasargs.__dict__ and imasargs.uri: - param = get_details_from_uri(imasargs.uri) - if param["pathPresent"]: - _file_name += f"PATH_{param['path'].replace('/', '_')}_" - else: - _file_name += f"PULSE_{param['pulse']}_RUN_{param['run']}_" - else: - _file_name += f"PULSE_{imasargs.pulse}_RUN_{imasargs.run}_" - if time_value: - _file_name += f"TIME_{time_value:.3f}" - _file_name += ".png" - return _file_name + tool_name = title or "plot" + timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") + return f"{tool_name}_{timestamp}.png" + + +def _is_interactive_backend(canvas): + """Return True for GUI and notebook widget backends.""" + import matplotlib + + backend = matplotlib.get_backend().lower() + if backend in ("widget", "ipympl", "module://ipympl.backend_nbagg"): + return True + return getattr(canvas.fig.canvas, "required_interactive_framework", None) is not None def show_plot(canvas, imasargs, title="", time_value=None, fname=None, show_kwargs=None): - """Save non-interactive canvases automatically, otherwise show the plot.""" - noninteractive = getattr(canvas.fig.canvas, "required_interactive_framework", None) is None - if not imasargs.save and not noninteractive: + """Display interactive plots and save non-interactive plots automatically.""" + noninteractive = not _is_interactive_backend(canvas) + if not imasargs.save and (_is_jupyter() or not noninteractive): canvas.show(**(show_kwargs or {})) return + automatic_save = noninteractive and not imasargs.save if fname is None: fname = get_file_name(imasargs, title, time_value) - if noninteractive and not imasargs.save: + if automatic_save: extension = canvas.fig.canvas.get_default_filetype() fname = f"{os.path.splitext(fname)[0]}.{extension}" if imasargs.directory: os.makedirs(imasargs.directory, exist_ok=True) fname = os.path.join(imasargs.directory, fname) + if automatic_save: + import matplotlib + + logger.info( + "Non-interactive Matplotlib backend '%s' detected; saving figure to %s", + matplotlib.get_backend(), + fname, + ) canvas.save(fname) diff --git a/idstools/utils/idshelper.py b/idstools/utils/idshelper.py index c4b0c6e7..e2c41956 100644 --- a/idstools/utils/idshelper.py +++ b/idstools/utils/idshelper.py @@ -447,8 +447,12 @@ def get_available_ids_and_times(db_entry_object) -> list: time_array = [-np.inf] except Exception as e: logger.debug(f"{e}") - time_array = [] - logger.info(f"ERROR! IDS {_ids_name} : Reading time array fails due to following problem : {e}") + time_array = None + logger.warning( + "Unable to read the time array for IDS %s: %s", + _ids_name, + e, + ) if occurrence != 0: result.append((f"{_ids_name}/{occurrence}", time_array)) else: diff --git a/idstools/utils/matplotlib_backend.py b/idstools/utils/matplotlib_backend.py new file mode 100644 index 00000000..0b726b1a --- /dev/null +++ b/idstools/utils/matplotlib_backend.py @@ -0,0 +1,47 @@ +"""Configure Matplotlib before importing Matplotlib itself.""" + +import ast +import os +import sys + + +def _backend_from_cli_rc(argv): + """Return a backend requested by a ``--rc backend=...`` option.""" + for index, argument in enumerate(argv): + if argument == "--rc" and index + 1 < len(argv): + rc_string = argv[index + 1] + elif argument.startswith("--rc="): + rc_string = argument.split("=", 1)[1] + else: + continue + for item in rc_string.split(";"): + key, separator, value = item.partition("=") + if separator and key.strip() == "backend": + value = value.strip() + try: + value = ast.literal_eval(value) + except (SyntaxError, ValueError): + pass + return str(value) + return None + + +def _configure_backend_from_cli_rc(argv=None): + """Set ``MPLBACKEND`` from command-line rcParams before Matplotlib loads.""" + requested_backend = _backend_from_cli_rc(sys.argv if argv is None else argv) + if requested_backend: + os.environ["MPLBACKEND"] = requested_backend + return requested_backend + + +def _is_jupyter(): + """Return True if running inside a Jupyter notebook/lab/Colab kernel.""" + try: + from IPython import get_ipython + + shell = get_ipython() + if shell is None: + return False + return shell.__class__.__name__ in ("ZMQInteractiveShell", "Shell") + except ImportError: + return False diff --git a/idstools/view/common.py b/idstools/view/common.py index d38e79ac..67ad60f9 100644 --- a/idstools/view/common.py +++ b/idstools/view/common.py @@ -1,73 +1,16 @@ -import ast import logging import os import sys +from idstools.utils.matplotlib_backend import ( + _configure_backend_from_cli_rc, + _is_jupyter, +) -def _backend_from_cli_rc(argv): - """Return a backend requested by a --rc backend=... command-line option.""" - for index, argument in enumerate(argv): - if argument == "--rc" and index + 1 < len(argv): - rc_string = argv[index + 1] - elif argument.startswith("--rc="): - rc_string = argument.split("=", 1)[1] - else: - continue - for item in rc_string.split(";"): - key, separator, value = item.partition("=") - if separator and key.strip() == "backend": - value = value.strip() - try: - value = ast.literal_eval(value) - except (SyntaxError, ValueError): - pass - return str(value) - return None - - -_requested_backend = _backend_from_cli_rc(sys.argv) -if _requested_backend: - os.environ["MPLBACKEND"] = _requested_backend +_configure_backend_from_cli_rc() import matplotlib # noqa: E402 - backend env must be set before importing matplotlib - -def _is_jupyter() -> bool: - """Return True if running inside a Jupyter notebook/lab/Colab kernel.""" - try: - from IPython import get_ipython - - shell = get_ipython() - if shell is None: - return False - shell_class = shell.__class__.__name__ - # ZMQInteractiveShell: Jupyter Notebook/Lab - # Shell: Google Colab - return shell_class in ("ZMQInteractiveShell", "Shell") - except ImportError: - return False - - -if not os.environ.get("MPLBACKEND"): - if _is_jupyter(): - if "matplotlib.pyplot" not in sys.modules: - try: - import ipympl # noqa: F401 - imported to check availability - - matplotlib.use("widget") - except ImportError: - matplotlib.use("agg") - elif sys.platform.startswith("win") or "DISPLAY" in os.environ: - - try: - import tkinter # noqa: F401 - imported to check availability - - matplotlib.use("TkAgg") - except (ImportError, ModuleNotFoundError): - matplotlib.use("agg") - else: - matplotlib.use("agg") - import matplotlib.pyplot as plt # noqa: E402 logger = logging.getLogger("module") @@ -266,8 +209,8 @@ def show(self, *args, **kwargs): None Notes: - Uses the TkAgg backend for window resizing when available. - Other backends (agg, Qt) may not support window maximization. + Window maximization depends on the backend selected by Matplotlib. + Some backends may not support it. Examples: >>> canvas = PlotCanvas() @@ -281,14 +224,14 @@ def show(self, *args, **kwargs): from IPython.display import display display(self.fig) - if backend != "module://matplotlib_ipympl.backend_nbagg": - plt.close("all") + if backend not in ("widget", "ipympl", "module://ipympl.backend_nbagg"): + plt.close(self.fig) except ImportError: pass return wm = self.get_current_fig_manager() try: - # Try to maximize the window (only works with TkAgg backend) + # Try to maximize the window when the active backend exposes one. window = wm.window screen_y = window.winfo_screenheight() screen_x = window.winfo_screenwidth()