diff --git a/CHANGES.rst b/CHANGES.rst index 0ffabf7ea3..57ece80d46 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -19,6 +19,8 @@ New Features - Add button in Plot Options to apply preset RBG options to visible layers when in Monochromatic mode. [#2558, #2568] +- Plugin "action" buttons disable and show icon indicating that an action is in progress. [#2560] + Cubeviz ^^^^^^^ diff --git a/jdaviz/components/plugin_add_results.vue b/jdaviz/components/plugin_add_results.vue index 693da081ee..c8db2dd96a 100644 --- a/jdaviz/components/plugin_add_results.vue +++ b/jdaviz/components/plugin_add_results.vue @@ -54,11 +54,21 @@ - {{action_label}}{{label_overwrite ? ' (Overwrite)' : ''}} + > + + + {{action_label}}{{label_overwrite ? ' (Overwrite)' : ''}} @@ -75,6 +85,6 @@ module.exports = { props: ['label', 'label_default', 'label_auto', 'label_invalid_msg', 'label_overwrite', 'label_label', 'label_hint', 'add_to_viewer_items', 'add_to_viewer_selected', 'add_to_viewer_hint', - 'action_disabled', 'action_label', 'action_tooltip'] + 'action_disabled', 'action_spinner', 'action_label', 'action_tooltip'] }; diff --git a/jdaviz/configs/cubeviz/plugins/moment_maps/moment_maps.py b/jdaviz/configs/cubeviz/plugins/moment_maps/moment_maps.py index 93243585e4..e7aefc0a22 100644 --- a/jdaviz/configs/cubeviz/plugins/moment_maps/moment_maps.py +++ b/jdaviz/configs/cubeviz/plugins/moment_maps/moment_maps.py @@ -13,7 +13,8 @@ from jdaviz.core.template_mixin import (PluginTemplateMixin, DatasetSelectMixin, SpectralSubsetSelectMixin, - AddResultsMixin) + AddResultsMixin, + with_spinner) from jdaviz.core.user_api import PluginUserApi __all__ = ['MomentMap'] @@ -91,6 +92,7 @@ def _set_default_results_label(self, event={}): label_comps += [f"moment {self.n_moment}"] self.results_label_default = " ".join(label_comps) + @with_spinner() def calculate_moment(self, add_data=True): """ Calculate the moment map diff --git a/jdaviz/configs/cubeviz/plugins/moment_maps/moment_maps.vue b/jdaviz/configs/cubeviz/plugins/moment_maps/moment_maps.vue index 368842c4ee..523b1036a1 100644 --- a/jdaviz/configs/cubeviz/plugins/moment_maps/moment_maps.vue +++ b/jdaviz/configs/cubeviz/plugins/moment_maps/moment_maps.vue @@ -46,6 +46,7 @@ :add_to_viewer_selected.sync="add_to_viewer_selected" action_label="Calculate" action_tooltip="Calculate moment map" + :action_spinner="spinner" @click:action="calculate_moment" > diff --git a/jdaviz/configs/cubeviz/plugins/spectral_extraction/spectral_extraction.py b/jdaviz/configs/cubeviz/plugins/spectral_extraction/spectral_extraction.py index 50ee471fc0..7170e45431 100644 --- a/jdaviz/configs/cubeviz/plugins/spectral_extraction/spectral_extraction.py +++ b/jdaviz/configs/cubeviz/plugins/spectral_extraction/spectral_extraction.py @@ -12,7 +12,8 @@ from jdaviz.core.template_mixin import (PluginTemplateMixin, SelectPluginComponent, SpatialSubsetSelectMixin, - AddResultsMixin) + AddResultsMixin, + with_spinner) from jdaviz.core.user_api import PluginUserApi from jdaviz.configs.cubeviz.plugins.parsers import _return_spectrum_with_correct_units @@ -72,6 +73,7 @@ def user_api(self): ) ) + @with_spinner() def collapse_to_spectrum(self, add_data=True, **kwargs): """ Collapse over the spectral axis. diff --git a/jdaviz/configs/cubeviz/plugins/spectral_extraction/spectral_extraction.vue b/jdaviz/configs/cubeviz/plugins/spectral_extraction/spectral_extraction.vue index 90507eb458..4e46c6cdbc 100644 --- a/jdaviz/configs/cubeviz/plugins/spectral_extraction/spectral_extraction.vue +++ b/jdaviz/configs/cubeviz/plugins/spectral_extraction/spectral_extraction.vue @@ -36,6 +36,7 @@ :add_to_viewer_selected.sync="add_to_viewer_selected" action_label="Extract" action_tooltip="Run spectral extraction with error and mask propagation" + :action_spinner="spinner" @click:action="spectral_extraction" > diff --git a/jdaviz/configs/default/plugins/collapse/collapse.py b/jdaviz/configs/default/plugins/collapse/collapse.py index 82a4233c50..438889a695 100644 --- a/jdaviz/configs/default/plugins/collapse/collapse.py +++ b/jdaviz/configs/default/plugins/collapse/collapse.py @@ -11,7 +11,8 @@ DatasetSelectMixin, SelectPluginComponent, SpectralSubsetSelectMixin, - AddResultsMixin) + AddResultsMixin, + with_spinner) from jdaviz.core.user_api import PluginUserApi __all__ = ['Collapse'] @@ -69,6 +70,7 @@ def _set_default_results_label(self, event={}): label_comps += ["collapsed"] self.results_label_default = " ".join(label_comps) + @with_spinner() def collapse(self, add_data=True): """ Collapse over the spectral axis. diff --git a/jdaviz/configs/default/plugins/collapse/collapse.vue b/jdaviz/configs/default/plugins/collapse/collapse.vue index 2c784f1be2..0827e3ee60 100644 --- a/jdaviz/configs/default/plugins/collapse/collapse.vue +++ b/jdaviz/configs/default/plugins/collapse/collapse.vue @@ -45,6 +45,7 @@ :add_to_viewer_selected.sync="add_to_viewer_selected" action_label="Collapse" action_tooltip="Collapse data" + :action_spinner="spinner" @click:action="collapse" > diff --git a/jdaviz/configs/default/plugins/export_plot/export_plot.py b/jdaviz/configs/default/plugins/export_plot/export_plot.py index 83df8e9eae..f3d1f438de 100644 --- a/jdaviz/configs/default/plugins/export_plot/export_plot.py +++ b/jdaviz/configs/default/plugins/export_plot/export_plot.py @@ -7,7 +7,7 @@ from jdaviz.core.custom_traitlets import FloatHandleEmpty, IntHandleEmpty from jdaviz.core.events import AddDataMessage, SnackbarMessage from jdaviz.core.registries import tray_registry -from jdaviz.core.template_mixin import PluginTemplateMixin, ViewerSelectMixin +from jdaviz.core.template_mixin import PluginTemplateMixin, ViewerSelectMixin, with_spinner from jdaviz.core.user_api import PluginUserApi try: @@ -146,6 +146,7 @@ def vue_save_figure(self, filetype): """ self.save_figure(filetype=filetype) + @with_spinner('movie_recording') def _save_movie(self, i_start, i_end, fps, filename, rm_temp_files): # NOTE: All the stuff here has to be in the same thread but # separate from main app thread to work. @@ -161,8 +162,6 @@ def _save_movie(self, i_start, i_end, fps, filename, rm_temp_files): i_step = 1 # Need n_frames check if we allow tweaking try: - self.movie_recording = True - while i <= i_end: if self.movie_interrupt: break @@ -190,7 +189,6 @@ def _save_movie(self, i_start, i_end, fps, filename, rm_temp_files): if video: video.release() slice_plg._on_slider_updated({'new': orig_slice}) - self.movie_recording = False if rm_temp_files or self.movie_interrupt: for cur_pngfile in temp_png_files: diff --git a/jdaviz/configs/default/plugins/gaussian_smooth/gaussian_smooth.py b/jdaviz/configs/default/plugins/gaussian_smooth/gaussian_smooth.py index a117731f11..718e150d84 100644 --- a/jdaviz/configs/default/plugins/gaussian_smooth/gaussian_smooth.py +++ b/jdaviz/configs/default/plugins/gaussian_smooth/gaussian_smooth.py @@ -10,7 +10,8 @@ from jdaviz.core.events import SnackbarMessage from jdaviz.core.registries import tray_registry from jdaviz.core.template_mixin import (PluginTemplateMixin, DatasetSelectMixin, - SelectPluginComponent, AddResultsMixin) + SelectPluginComponent, AddResultsMixin, + with_spinner) from jdaviz.core.user_api import PluginUserApi __all__ = ['GaussianSmooth'] @@ -176,6 +177,7 @@ def smooth(self, add_data=True): return results + @with_spinner() def spectral_smooth(self): """ Smooth the input spectrum along the spectral axis. To add the resulting spectrum into @@ -199,6 +201,7 @@ def spectral_smooth(self): return spec_smoothed + @with_spinner('spinner') def spatial_smooth(self): """ Use astropy convolution machinery to smooth the spatial dimensions of diff --git a/jdaviz/configs/default/plugins/gaussian_smooth/gaussian_smooth.vue b/jdaviz/configs/default/plugins/gaussian_smooth/gaussian_smooth.vue index fadcbfd2f4..aea6e25314 100644 --- a/jdaviz/configs/default/plugins/gaussian_smooth/gaussian_smooth.vue +++ b/jdaviz/configs/default/plugins/gaussian_smooth/gaussian_smooth.vue @@ -50,6 +50,7 @@ :add_to_viewer_selected.sync="add_to_viewer_selected" action_label="Smooth" action_tooltip="Smooth data" + :action_spinner="spinner" @click:action="apply" > diff --git a/jdaviz/configs/default/plugins/model_fitting/model_fitting.py b/jdaviz/configs/default/plugins/model_fitting/model_fitting.py index ed01db6554..241932da63 100644 --- a/jdaviz/configs/default/plugins/model_fitting/model_fitting.py +++ b/jdaviz/configs/default/plugins/model_fitting/model_fitting.py @@ -19,7 +19,8 @@ NonFiniteUncertaintyMismatchMixin, AutoTextField, AddResultsMixin, - TableMixin) + TableMixin, + with_spinner) from jdaviz.core.custom_traitlets import IntHandleEmpty from jdaviz.core.user_api import PluginUserApi from jdaviz.configs.default.plugins.model_fitting.fitting_backend import fit_model_to_spectrum @@ -767,6 +768,7 @@ def _update_viewer_filters(self, event={}): # only want spectral viewers in the options self.add_results.viewer.filters = ['is_spectrum_viewer'] + @with_spinner() def calculate_fit(self, add_data=True): """ Calculate the fit. diff --git a/jdaviz/configs/default/plugins/model_fitting/model_fitting.vue b/jdaviz/configs/default/plugins/model_fitting/model_fitting.vue index 4b3ba89e13..921a627e3a 100644 --- a/jdaviz/configs/default/plugins/model_fitting/model_fitting.vue +++ b/jdaviz/configs/default/plugins/model_fitting/model_fitting.vue @@ -261,6 +261,7 @@ action_label="Fit Model" action_tooltip="Fit the model to the data" :action_disabled="model_equation_invalid_msg.length > 0 || !spectral_subset_valid" + :action_spinner="spinner" @click:action="apply" >
diff --git a/jdaviz/configs/imviz/plugins/aper_phot_simple/aper_phot_simple.py b/jdaviz/configs/imviz/plugins/aper_phot_simple/aper_phot_simple.py index 2424e67025..96b709b467 100644 --- a/jdaviz/configs/imviz/plugins/aper_phot_simple/aper_phot_simple.py +++ b/jdaviz/configs/imviz/plugins/aper_phot_simple/aper_phot_simple.py @@ -22,7 +22,7 @@ from jdaviz.core.region_translators import regions2aperture, _get_region_from_spatial_subset from jdaviz.core.registries import tray_registry from jdaviz.core.template_mixin import (PluginTemplateMixin, DatasetMultiSelectMixin, - SubsetSelect, TableMixin, PlotMixin) + SubsetSelect, TableMixin, PlotMixin, with_spinner) from jdaviz.core.tools import ICON_DIR from jdaviz.utils import PRIHDR_KEY @@ -305,6 +305,7 @@ def _background_selected_changed(self, event={}): self.hub.broadcast(SnackbarMessage( f"Failed to extract {background_selected}: {repr(e)}", color='error', sender=self)) + @with_spinner() def calculate_photometry(self, dataset=None, aperture=None, background=None, background_value=None, pixel_area=None, counts_factor=None, flux_scaling=None, add_to_table=True, update_plots=True): @@ -726,6 +727,7 @@ def _unpack_dict_list(mult_values, single_values): return _unpack_dict_list(mult_values, single_values) + @with_spinner() def calculate_batch_photometry(self, options=[], add_to_table=True, update_plots=True, full_exceptions=False): """ diff --git a/jdaviz/configs/imviz/plugins/aper_phot_simple/aper_phot_simple.vue b/jdaviz/configs/imviz/plugins/aper_phot_simple/aper_phot_simple.vue index 505cdcbfa1..32d867a064 100644 --- a/jdaviz/configs/imviz/plugins/aper_phot_simple/aper_phot_simple.vue +++ b/jdaviz/configs/imviz/plugins/aper_phot_simple/aper_phot_simple.vue @@ -154,7 +154,7 @@ - Calculate + Calculate
diff --git a/jdaviz/configs/imviz/plugins/catalogs/catalogs.py b/jdaviz/configs/imviz/plugins/catalogs/catalogs.py index 8c6b619c17..b8d9f0d364 100644 --- a/jdaviz/configs/imviz/plugins/catalogs/catalogs.py +++ b/jdaviz/configs/imviz/plugins/catalogs/catalogs.py @@ -8,7 +8,8 @@ from jdaviz.core.events import SnackbarMessage from jdaviz.core.registries import tray_registry from jdaviz.core.template_mixin import (PluginTemplateMixin, ViewerSelectMixin, - FileImportSelectPluginComponent, HasFileImportSelect) + FileImportSelectPluginComponent, HasFileImportSelect, + with_spinner) __all__ = ['Catalogs'] @@ -55,6 +56,7 @@ def _file_parser(path): return '', {path: table} + @with_spinner() def search(self): """ Search the catalog, display markers on the viewer, and return a table of results (or None diff --git a/jdaviz/configs/imviz/plugins/catalogs/catalogs.vue b/jdaviz/configs/imviz/plugins/catalogs/catalogs.vue index b55fce7eb7..092c3398ce 100644 --- a/jdaviz/configs/imviz/plugins/catalogs/catalogs.vue +++ b/jdaviz/configs/imviz/plugins/catalogs/catalogs.vue @@ -32,7 +32,7 @@ Clear - Search + Search diff --git a/jdaviz/configs/specviz2d/plugins/spectral_extraction/spectral_extraction.py b/jdaviz/configs/specviz2d/plugins/spectral_extraction/spectral_extraction.py index b3de261c5d..f3db52f20c 100644 --- a/jdaviz/configs/specviz2d/plugins/spectral_extraction/spectral_extraction.py +++ b/jdaviz/configs/specviz2d/plugins/spectral_extraction/spectral_extraction.py @@ -9,7 +9,8 @@ SelectPluginComponent, DatasetSelect, AddResults, - skip_if_no_updates_since_last_active) + skip_if_no_updates_since_last_active, + with_spinner) from jdaviz.core.user_api import PluginUserApi from jdaviz.core.custom_traitlets import IntHandleEmpty, FloatHandleEmpty from jdaviz.core.marks import PluginLine @@ -130,6 +131,7 @@ class SpectralExtraction(PluginTemplateMixin): trace_results_label_overwrite = Bool().tag(sync=True) trace_add_to_viewer_items = List().tag(sync=True) trace_add_to_viewer_selected = Unicode().tag(sync=True) + trace_spinner = Bool(False).tag(sync=True) # BACKGROUND bg_dataset_items = List().tag(sync=True) @@ -156,6 +158,7 @@ class SpectralExtraction(PluginTemplateMixin): bg_results_label_overwrite = Bool().tag(sync=True) bg_add_to_viewer_items = List().tag(sync=True) bg_add_to_viewer_selected = Unicode().tag(sync=True) + bg_img_spinner = Bool(False).tag(sync=True) bg_spec_results_label = Unicode().tag(sync=True) bg_spec_results_label_default = Unicode().tag(sync=True) @@ -164,6 +167,7 @@ class SpectralExtraction(PluginTemplateMixin): bg_spec_results_label_overwrite = Bool().tag(sync=True) bg_spec_add_to_viewer_items = List().tag(sync=True) bg_spec_add_to_viewer_selected = Unicode().tag(sync=True) + bg_spec_spinner = Bool(False).tag(sync=True) bg_sub_results_label = Unicode().tag(sync=True) bg_sub_results_label_default = Unicode().tag(sync=True) @@ -172,6 +176,7 @@ class SpectralExtraction(PluginTemplateMixin): bg_sub_results_label_overwrite = Bool().tag(sync=True) bg_sub_add_to_viewer_items = List().tag(sync=True) bg_sub_add_to_viewer_selected = Unicode().tag(sync=True) + bg_sub_spinner = Bool(False).tag(sync=True) # EXTRACT ext_dataset_items = List().tag(sync=True) @@ -195,6 +200,7 @@ class SpectralExtraction(PluginTemplateMixin): ext_results_label_overwrite = Bool().tag(sync=True) ext_add_to_viewer_items = List().tag(sync=True) ext_add_to_viewer_selected = Unicode().tag(sync=True) + # uses default "spinner" def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) @@ -682,6 +688,7 @@ def import_trace(self, trace): else: # pragma: no cover raise NotImplementedError(f"trace of type {trace.__class__.__name__} not supported") + @with_spinner('trace_spinner') def export_trace(self, add_data=False, **kwargs): """ Create a specreduce Trace object from the input parameters @@ -780,6 +787,7 @@ def import_bg(self, bg): self.bg_width = bg.width + @with_spinner('bg_spinner') def export_bg(self, **kwargs): """ Create a specreduce Background object from the input parameters defined in the plugin. @@ -817,6 +825,7 @@ def export_bg(self, **kwargs): return bg + @with_spinner('bg_img_spinner') def export_bg_img(self, add_data=False, **kwargs): """ Create a background 2D spectrum from the input parameters defined in the plugin. @@ -843,6 +852,7 @@ def vue_create_bg_img(self, *args): color='error', sender=self) ) + @with_spinner('bg_spec_spinner') def export_bg_spectrum(self, add_data=False, **kwargs): """ Create a background 1D spectrum from the input parameters defined in the plugin. @@ -863,6 +873,7 @@ def export_bg_spectrum(self, add_data=False, **kwargs): def vue_create_bg_spec(self, *args): self.export_bg_spectrum(add_data=True) + @with_spinner('bg_sub_spinner') def export_bg_sub(self, add_data=False, **kwargs): """ Create a background-subtracted 2D spectrum from the input parameters defined in the plugin. @@ -936,6 +947,7 @@ def export_extract(self, **kwargs): return ext + @with_spinner('spinner') def export_extract_spectrum(self, add_data=False, **kwargs): """ Create an extracted 1D spectrum from the input parameters defined in the plugin. diff --git a/jdaviz/configs/specviz2d/plugins/spectral_extraction/spectral_extraction.vue b/jdaviz/configs/specviz2d/plugins/spectral_extraction/spectral_extraction.vue index b9bbd659b3..47cb055262 100644 --- a/jdaviz/configs/specviz2d/plugins/spectral_extraction/spectral_extraction.vue +++ b/jdaviz/configs/specviz2d/plugins/spectral_extraction/spectral_extraction.vue @@ -179,6 +179,7 @@ :add_to_viewer_selected.sync="trace_add_to_viewer_selected" action_label="Create" action_tooltip="Create Trace" + :action_spinner="trace_spinner" @click:action="create_trace" > @@ -293,6 +294,7 @@ :add_to_viewer_selected.sync="bg_add_to_viewer_selected" action_label="Export" action_tooltip="Create Background Image" + :action_spinner="bg_img_spinner" @click:action="create_bg_img" > @@ -415,6 +417,7 @@ action_label="Extract" action_tooltip="Extract 1D Spectrum" :action_disabled="ext_specreduce_err.length > 0" + :action_spinner="spinner" @click:action="extract_spectrum" > diff --git a/jdaviz/core/template_mixin.py b/jdaviz/core/template_mixin.py index e36d2d1951..2944e2dc27 100644 --- a/jdaviz/core/template_mixin.py +++ b/jdaviz/core/template_mixin.py @@ -44,7 +44,7 @@ __all__ = ['show_widget', 'TemplateMixin', 'PluginTemplateMixin', - 'skip_if_no_updates_since_last_active', + 'skip_if_no_updates_since_last_active', 'with_spinner', 'ViewerPropertiesMixin', 'BasePluginComponent', 'SelectPluginComponent', 'UnitSelectPluginComponent', 'EditableSelectPluginComponent', @@ -266,6 +266,28 @@ def wrapper(self, msg={}): return decorator +def with_spinner(spinner_traitlet='spinner'): + """ + decorator on a plugin method to set a traitlet to True at the beginning + and False either on failure or successful completion. This traitlet can + then be used in the UI to disable elements or display a spinner during + operation. Each plugin gets a 'spinner' traitlet by default, but some plugins + may want different controls for different sections/actions within the plugin. + """ + def decorator(meth): + def wrapper(self, *args, **kwargs): + setattr(self, spinner_traitlet, True) + try: + ret_ = meth(self, *args, **kwargs) + except Exception: + setattr(self, spinner_traitlet, False) + raise + setattr(self, spinner_traitlet, False) + return ret_ + return wrapper + return decorator + + class PluginTemplateMixin(TemplateMixin): """ This base class can be inherited by all sidebar/tray plugins to expose common functionality. @@ -276,6 +298,7 @@ class PluginTemplateMixin(TemplateMixin): uses_active_status = Bool(False).tag(sync=True) # noqa whether the plugin has live-preview marks, set to True in plugins to expose keep_active switch keep_active = Bool(False).tag(sync=True) # noqa whether the live-preview marks show regardless of active state, inapplicable unless uses_active_status is True is_active = Bool(False).tag(sync=True) # noqa read-only: whether the previews should be shown according to plugin_opened and keep_active + spinner = Bool(False).tag(sync=True) # noqa use along-side @with_spinner() and def __init__(self, **kwargs): self._viewer_callbacks = {}