diff --git a/CHANGES.rst b/CHANGES.rst
index daba79a73f..cf107b003a 100644
--- a/CHANGES.rst
+++ b/CHANGES.rst
@@ -30,6 +30,8 @@ Cubeviz
- New aperture photometry plugin that can perform aperture photometry on selected cube slice. [#2666]
+- Live previews in spectral extraction plugin. [#2733]
+
Imviz
^^^^^
diff --git a/jdaviz/app.py b/jdaviz/app.py
index cc0400d22a..cc4dc0baf6 100644
--- a/jdaviz/app.py
+++ b/jdaviz/app.py
@@ -126,6 +126,7 @@ def to_unit(self, data, cid, values, original_units, target_units):
'j-plugin-section-header': 'components/plugin_section_header.vue',
'j-number-uncertainty': 'components/number_uncertainty.vue',
'j-plugin-popout': 'components/plugin_popout.vue',
+ 'plugin-previews-temp-disabled': 'components/plugin_previews_temp_disabled.vue', # noqa
'plugin-table': 'components/plugin_table.vue',
'plugin-dataset-select': 'components/plugin_dataset_select.vue',
'plugin-subset-select': 'components/plugin_subset_select.vue',
diff --git a/jdaviz/components/plugin_previews_temp_disabled.vue b/jdaviz/components/plugin_previews_temp_disabled.vue
new file mode 100644
index 0000000000..0aed4c84a7
--- /dev/null
+++ b/jdaviz/components/plugin_previews_temp_disabled.vue
@@ -0,0 +1,25 @@
+
+
+ Live-updating is temporarily disabled (last update took {{previews_last_time}}s)
+
+
+ {$emit('update:show_live_preview', false); $emit('disable_previews')}">
+ disable previews
+
+
+
+
+
+
+ update preview
+
+
+
+
+
+
+
diff --git a/jdaviz/configs/cubeviz/plugins/spectral_extraction/spectral_extraction.py b/jdaviz/configs/cubeviz/plugins/spectral_extraction/spectral_extraction.py
index 36d1210337..851a9bae37 100644
--- a/jdaviz/configs/cubeviz/plugins/spectral_extraction/spectral_extraction.py
+++ b/jdaviz/configs/cubeviz/plugins/spectral_extraction/spectral_extraction.py
@@ -14,6 +14,7 @@
from jdaviz.core.custom_traitlets import FloatHandleEmpty
from jdaviz.core.events import SnackbarMessage, SliceValueUpdatedMessage
+from jdaviz.core.marks import SpectralExtractionPreview
from jdaviz.core.registries import tray_registry
from jdaviz.core.template_mixin import (PluginTemplateMixin,
DatasetSelectMixin,
@@ -21,10 +22,12 @@
ApertureSubsetSelectMixin,
ApertureSubsetSelect,
AddResultsMixin,
- with_spinner)
+ skip_if_no_updates_since_last_active,
+ with_spinner, with_temp_disable)
from jdaviz.core.user_api import PluginUserApi
from jdaviz.core.region_translators import regions2aperture
from jdaviz.configs.cubeviz.plugins.parsers import _return_spectrum_with_correct_units
+from jdaviz.configs.cubeviz.plugins.viewers import CubevizProfileView
__all__ = ['SpectralExtraction']
@@ -59,6 +62,7 @@ class SpectralExtraction(PluginTemplateMixin, ApertureSubsetSelectMixin,
"""
template_file = __file__, "spectral_extraction.vue"
uses_active_status = Bool(True).tag(sync=True)
+ show_live_preview = Bool(True).tag(sync=True)
# feature flag for background cone support
dev_bg_support = Bool(False).tag(sync=True) # when enabling: add entries to docstring
@@ -458,3 +462,60 @@ def _set_default_results_label(self, event={}):
):
label += f' ({self.aperture_selected})'
self.results_label_default = label
+
+ @property
+ def marks(self):
+ marks = {}
+ for id, viewer in self.app._viewer_store.items():
+ if not isinstance(viewer, CubevizProfileView):
+ continue
+ for mark in viewer.figure.marks:
+ if isinstance(mark, SpectralExtractionPreview):
+ marks[id] = mark
+ break
+ else:
+ mark = SpectralExtractionPreview(viewer, visible=self.is_active)
+ viewer.figure.marks = viewer.figure.marks + [mark]
+ marks[id] = mark
+ return marks
+
+ def _clear_marks(self):
+ for mark in self.marks.values():
+ if mark.visible:
+ mark.clear()
+ mark.visible = False
+
+ @observe('is_active', 'show_live_preview')
+ def _toggle_marks(self, event={}):
+ visible = self.show_live_preview and self.is_active
+
+ if not visible:
+ self._clear_marks()
+ elif event.get('name', '') in ('is_active', 'show_live_preview'):
+ # then the marks themselves need to be updated
+ self._live_update(event)
+
+ @observe('aperture_selected', 'function_selected',
+ 'wavelength_dependent', 'reference_wavelength',
+ 'aperture_method_selected',
+ 'previews_temp_disabled')
+ @skip_if_no_updates_since_last_active()
+ @with_temp_disable(timeout=0.3)
+ def _live_update(self, event={}):
+ if not self.show_live_preview or not self.is_active:
+ self._clear_marks()
+ return
+
+ if event.get('name', '') not in ('is_active', 'show_live_preview'):
+ # mark visibility hasn't been handled yet
+ self._toggle_marks()
+
+ try:
+ sp = self.collapse_to_spectrum(add_data=False)
+ except Exception:
+ self._clear_marks()
+ return
+
+ for mark in self.marks.values():
+ mark.update_xy(sp.spectral_axis.value, sp.flux.value)
+ mark.visible = True
diff --git a/jdaviz/configs/cubeviz/plugins/spectral_extraction/spectral_extraction.vue b/jdaviz/configs/cubeviz/plugins/spectral_extraction/spectral_extraction.vue
index 8698db0b57..75179e3c8e 100644
--- a/jdaviz/configs/cubeviz/plugins/spectral_extraction/spectral_extraction.vue
+++ b/jdaviz/configs/cubeviz/plugins/spectral_extraction/spectral_extraction.vue
@@ -8,6 +8,26 @@
:popout_button="popout_button"
:disabled_msg="disabled_msg">
+
+
+
+
+ Settings
+
+
+
+
+
+
+
+
+
+
active_step='ap'">
Aperture
@@ -166,7 +186,11 @@
-
+
+ """
+ def decorator(meth):
+ def wrapper(self, *args, **kwargs):
+ if getattr(self, disable_traitlet):
+ return
+ start = time.time()
+ ret_ = meth(self, *args, **kwargs)
+ exec_time = np.round(time.time() - start, 2)
+ setattr(self, time_traitlet, exec_time)
+ if exec_time > timeout:
+ setattr(self, disable_traitlet, True)
+ return ret_
+ return wrapper
+ return decorator
+
+
class PluginTemplateMixin(TemplateMixin):
"""
This base class can be inherited by all sidebar/tray plugins to expose common functionality.
@@ -319,6 +350,8 @@ class PluginTemplateMixin(TemplateMixin):
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
+ previews_temp_disabled = Bool(False).tag(sync=True) # noqa use along-side @with_temp_disable() and
+ previews_last_time = Float(0).tag(sync=True)
def __init__(self, **kwargs):
self._viewer_callbacks = {}