From 926f55ba1ddfd571d1eceac628383f47d02e7895 Mon Sep 17 00:00:00 2001 From: Jammy2211 Date: Fri, 3 Apr 2026 14:43:24 +0100 Subject: [PATCH] Make output_format configurable, default to "show" for interactive display Add output_format config option to visualize/general.yaml so users get interactive matplotlib windows by default instead of silently saving PNGs. Tests pass output_format="png" explicitly. Co-Authored-By: Claude Opus 4.6 (1M context) --- autoarray/config/visualize/general.yaml | 1 + autoarray/dataset/plot/imaging_plots.py | 4 +- .../dataset/plot/interferometer_plots.py | 4 +- autoarray/fit/plot/fit_imaging_plots.py | 2 +- .../fit/plot/fit_interferometer_plots.py | 4 +- autoarray/inversion/plot/inversion_plots.py | 4 +- autoarray/inversion/plot/mapper_plots.py | 4 +- autoarray/plot/array.py | 2 +- autoarray/plot/grid.py | 2 +- autoarray/plot/inversion.py | 2 +- autoarray/plot/utils.py | 53 +++++++++++++------ autoarray/plot/yx.py | 2 +- 12 files changed, 53 insertions(+), 31 deletions(-) diff --git a/autoarray/config/visualize/general.yaml b/autoarray/config/visualize/general.yaml index b3d14672..0ba855dd 100644 --- a/autoarray/config/visualize/general.yaml +++ b/autoarray/config/visualize/general.yaml @@ -5,6 +5,7 @@ general: log10_min_value: 1.0e-4 # If negative values are being plotted on a log10 scale, values below this value are rounded up to it (e.g. to remove negative values). log10_max_value: 1.0e99 # If positive values are being plotted on a log10 scale, values above this value are rounded down to it. zoom_around_mask: true # If True, plots of data structures with a mask automatically zoom in the masked region. + output_format: show # Default output format: "show" displays the figure interactively via plt.show(), "png"/"pdf"/etc. saves to file. inversion: reconstruction_vmax_factor: 0.5 total_mappings_pixels: 8 # The number of source pixels used when plotting the subplot_mappings of a pixelization. diff --git a/autoarray/dataset/plot/imaging_plots.py b/autoarray/dataset/plot/imaging_plots.py index ff9d271f..4271adb1 100644 --- a/autoarray/dataset/plot/imaging_plots.py +++ b/autoarray/dataset/plot/imaging_plots.py @@ -9,7 +9,7 @@ def subplot_imaging_dataset( dataset, output_path: Optional[str] = None, output_filename: str = "dataset", - output_format: str = "png", + output_format: str = None, colormap=None, use_log10: bool = False, grid=None, @@ -148,7 +148,7 @@ def subplot_imaging_dataset_list( dataset_list, output_path=None, output_filename: str = "dataset_combined", - output_format="png", + output_format=None, ): """ nĂ—3 subplot showing core components for each dataset in a list. diff --git a/autoarray/dataset/plot/interferometer_plots.py b/autoarray/dataset/plot/interferometer_plots.py index 0288c8ed..33e336a0 100644 --- a/autoarray/dataset/plot/interferometer_plots.py +++ b/autoarray/dataset/plot/interferometer_plots.py @@ -14,7 +14,7 @@ def subplot_interferometer_dataset( dataset, output_path: Optional[str] = None, output_filename: str = "dataset", - output_format: str = "png", + output_format: str = None, colormap=None, use_log10: bool = False, ): @@ -95,7 +95,7 @@ def subplot_interferometer_dirty_images( dataset, output_path: Optional[str] = None, output_filename: str = "dirty_images", - output_format: str = "png", + output_format: str = None, colormap=None, use_log10: bool = False, ): diff --git a/autoarray/fit/plot/fit_imaging_plots.py b/autoarray/fit/plot/fit_imaging_plots.py index ce157de9..faa471f3 100644 --- a/autoarray/fit/plot/fit_imaging_plots.py +++ b/autoarray/fit/plot/fit_imaging_plots.py @@ -10,7 +10,7 @@ def subplot_fit_imaging( fit, output_path: Optional[str] = None, output_filename: str = "fit", - output_format: str = "png", + output_format: str = None, colormap=None, use_log10: bool = False, residuals_symmetric_cmap: bool = True, diff --git a/autoarray/fit/plot/fit_interferometer_plots.py b/autoarray/fit/plot/fit_interferometer_plots.py index 11c9623b..136122d2 100644 --- a/autoarray/fit/plot/fit_interferometer_plots.py +++ b/autoarray/fit/plot/fit_interferometer_plots.py @@ -12,7 +12,7 @@ def subplot_fit_interferometer( fit, output_path: Optional[str] = None, output_filename: str = "fit", - output_format: str = "png", + output_format: str = None, colormap=None, use_log10: bool = False, residuals_symmetric_cmap: bool = True, @@ -107,7 +107,7 @@ def subplot_fit_interferometer_dirty_images( fit, output_path: Optional[str] = None, output_filename: str = "fit_dirty_images", - output_format: str = "png", + output_format: str = None, colormap=None, use_log10: bool = False, residuals_symmetric_cmap: bool = True, diff --git a/autoarray/inversion/plot/inversion_plots.py b/autoarray/inversion/plot/inversion_plots.py index ea4c174b..e57d412f 100644 --- a/autoarray/inversion/plot/inversion_plots.py +++ b/autoarray/inversion/plot/inversion_plots.py @@ -21,7 +21,7 @@ def subplot_of_mapper( mapper_index: int = 0, output_path: Optional[str] = None, output_filename: str = "inversion", - output_format: str = "png", + output_format: str = None, colormap=None, use_log10: bool = False, mesh_grid=None, @@ -233,7 +233,7 @@ def subplot_mappings( pixelization_index: int = 0, output_path: Optional[str] = None, output_filename: str = "mappings", - output_format: str = "png", + output_format: str = None, colormap=None, use_log10: bool = False, mesh_grid=None, diff --git a/autoarray/inversion/plot/mapper_plots.py b/autoarray/inversion/plot/mapper_plots.py index 8d5a7fdb..7a7e5007 100644 --- a/autoarray/inversion/plot/mapper_plots.py +++ b/autoarray/inversion/plot/mapper_plots.py @@ -15,7 +15,7 @@ def plot_mapper( solution_vector=None, output_path: Optional[str] = None, output_filename: str = "mapper", - output_format: str = "png", + output_format: str = None, colormap=None, use_log10: bool = False, vmin=None, @@ -84,7 +84,7 @@ def subplot_image_and_mapper( image, output_path: Optional[str] = None, output_filename: str = "image_and_mapper", - output_format: str = "png", + output_format: str = None, colormap=None, use_log10: bool = False, mesh_grid=None, diff --git a/autoarray/plot/array.py b/autoarray/plot/array.py index 964da0c1..39c0e779 100644 --- a/autoarray/plot/array.py +++ b/autoarray/plot/array.py @@ -60,7 +60,7 @@ def plot_array( figsize: Optional[Tuple[int, int]] = None, output_path: Optional[str] = None, output_filename: str = "array", - output_format: str = "png", + output_format: str = None, ) -> None: """ Plot a 2D array (image) using ``plt.imshow``. diff --git a/autoarray/plot/grid.py b/autoarray/plot/grid.py index 33287202..01230344 100644 --- a/autoarray/plot/grid.py +++ b/autoarray/plot/grid.py @@ -40,7 +40,7 @@ def plot_grid( figsize: Optional[Tuple[int, int]] = None, output_path: Optional[str] = None, output_filename: str = "grid", - output_format: str = "png", + output_format: str = None, ) -> None: """ Plot a 2D grid of ``(y, x)`` coordinates as a scatter plot. diff --git a/autoarray/plot/inversion.py b/autoarray/plot/inversion.py index 484b446c..fa58b7a6 100644 --- a/autoarray/plot/inversion.py +++ b/autoarray/plot/inversion.py @@ -34,7 +34,7 @@ def plot_inversion_reconstruction( figsize: Optional[Tuple[int, int]] = None, output_path: Optional[str] = None, output_filename: str = "reconstruction", - output_format: str = "png", + output_format: str = None, ) -> None: """ Plot an inversion reconstruction using the appropriate mapper type. diff --git a/autoarray/plot/utils.py b/autoarray/plot/utils.py index 7190ae7b..f3438568 100644 --- a/autoarray/plot/utils.py +++ b/autoarray/plot/utils.py @@ -298,14 +298,14 @@ def _output_mode_save(fig, filename): return True -def subplot_save(fig, output_path, output_filename, output_format): +def subplot_save(fig, output_path, output_filename, output_format=None): """Save a subplot figure to disk, or display it, then close it. All ``subplot_*`` functions call this as their final step. When - *output_path* is non-empty the figure is written to - ``/.``; otherwise - ``plt.show()`` is called. ``plt.close(fig)`` is always called to - release memory. + *output_format* is ``"show"`` (the config default) or *output_path* + is empty, ``plt.show()`` is called; otherwise the figure is written to + ``/.``. + ``plt.close(fig)`` is always called to release memory. Parameters ---------- @@ -317,12 +317,18 @@ def subplot_save(fig, output_path, output_filename, output_format): output_filename Base file name without extension. output_format - File format string, e.g. ``"png"`` or ``"pdf"``. + File format string, e.g. ``"png"`` or ``"pdf"``. ``"show"`` + displays the figure interactively. ``None`` reads from config. """ + if output_format is None: + output_format = _conf_output_format() + if _output_mode_save(fig, output_filename): return - if output_path: + if output_format == "show" or not output_path: + plt.show() + else: os.makedirs(output_path, exist_ok=True) try: fig.savefig( @@ -334,8 +340,6 @@ def subplot_save(fig, output_path, output_filename, output_format): logger.warning( f"subplot_save: could not save {output_filename}.{output_format}: {exc}" ) - else: - plt.show() plt.close(fig) @@ -454,13 +458,14 @@ def save_figure( fig: plt.Figure, path: str, filename: str, - format: str = "png", + format: str = None, dpi: Optional[int] = None, ) -> None: """ Save *fig* to ``/.`` then close it. - If *path* is an empty string or ``None``, ``plt.show()`` is called instead. + If *format* is ``"show"`` (the config default) or *path* is empty/``None``, + ``plt.show()`` is called instead of saving. After either action ``plt.close(fig)`` is always called to free memory. For FITS output use ``fits_array`` (in ``autogalaxy.plot``) instead. @@ -476,10 +481,14 @@ def save_figure( format File format(s) passed to ``fig.savefig``. Either a single string (e.g. ``"png"``) or a list/tuple of strings (e.g. ``["png", "pdf"]``) - to save in multiple formats in one call. + to save in multiple formats in one call. ``"show"`` displays the + figure interactively. ``None`` reads the default from config. dpi Resolution in dots per inch. """ + if format is None: + format = _conf_output_format() + if dpi is None: from autoconf import conf dpi = int(conf.instance["visualize"]["general"]["general"]["dpi"]) @@ -487,10 +496,15 @@ def save_figure( if _output_mode_save(fig, filename): return - if path: + formats = format if isinstance(format, (list, tuple)) else [format] + + if all(f == "show" for f in formats) or not path: + plt.show() + else: os.makedirs(path, exist_ok=True) - formats = format if isinstance(format, (list, tuple)) else [format] for fmt in formats: + if fmt == "show": + continue try: fig.savefig( os.path.join(path, f"{filename}.{fmt}"), @@ -502,8 +516,6 @@ def save_figure( logger.warning( f"save_figure: could not save {filename}.{fmt}: {exc}" ) - else: - plt.show() plt.close(fig) @@ -787,6 +799,15 @@ def _conf_imshow_origin() -> str: return "upper" +def _conf_output_format() -> str: + """Return the default output_format from config (``"show"``, ``"png"``, etc.).""" + try: + from autoconf import conf + return conf.instance["visualize"]["general"]["general"]["output_format"] + except Exception: + return "show" + + def _conf_ticks(key: str, default: float) -> float: try: from autoconf import conf diff --git a/autoarray/plot/yx.py b/autoarray/plot/yx.py index b3cf3fcc..e80f5476 100644 --- a/autoarray/plot/yx.py +++ b/autoarray/plot/yx.py @@ -35,7 +35,7 @@ def plot_yx( figsize: Optional[Tuple[int, int]] = None, output_path: Optional[str] = None, output_filename: str = "yx", - output_format: str = "png", + output_format: str = None, ) -> None: """ Plot 1D y versus x data.