diff --git a/CHANGES.rst b/CHANGES.rst index ad8c812541..83f3b9f7d9 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -10,7 +10,8 @@ New Features in Jy [#1564] - User-friendly API access to plugins, with exposed functionality for: line analysis, gaussian - smooth, moment maps, compass, collapse, and metadata. [#1401, #1642, #1643, #1636, #1641, #1634] + smooth, moment maps, compass, collapse, metadata, and slice. + [#1401, #1642, #1643, #1636, #1641, #1634, #1635] Cubeviz ^^^^^^^ diff --git a/jdaviz/configs/cubeviz/plugins/slice/slice.py b/jdaviz/configs/cubeviz/plugins/slice/slice.py index 9a016129e8..a7f2c72076 100644 --- a/jdaviz/configs/cubeviz/plugins/slice/slice.py +++ b/jdaviz/configs/cubeviz/plugins/slice/slice.py @@ -9,22 +9,41 @@ from jdaviz.core.events import AddDataMessage, SliceToolStateMessage, SliceSelectSliceMessage from jdaviz.core.registries import tray_registry from jdaviz.core.template_mixin import PluginTemplateMixin +from jdaviz.core.user_api import PluginUserApi __all__ = ['Slice'] @tray_registry('cubeviz-slice', label="Slice") class Slice(PluginTemplateMixin): + """ + See the :ref:`Slice Plugin Documentation ` for more details. + + Only the following attributes and methods are available through the + :ref:`public plugin API `: + + * :meth:`~jdaviz.core.template_mixin.PluginTemplateMixin.show` + * :meth:`~jdaviz.core.template_mixin.PluginTemplateMixin.open_in_tray` + * ``slice`` + Current slice number. + * ``wavelength`` + Wavelength of the current slice. When setting this directly, it will update automatically to + the wavelength corresponding to the nearest slice. + * ``show_indicator`` + Whether to show indicator in spectral viewer when slice tool is inactive. + * ``show_wavelength`` + Whether to show slice wavelength in label to right of indicator. + """ template_file = __file__, "slice.vue" - slider = Any(0).tag(sync=True) + slice = Any(0).tag(sync=True) wavelength = Any(-1).tag(sync=True) wavelength_unit = Any("").tag(sync=True) min_value = Float(0).tag(sync=True) max_value = Float(100).tag(sync=True) wait = Int(200).tag(sync=True) - setting_show_indicator = Bool(True).tag(sync=True) - setting_show_wavelength = Bool(True).tag(sync=True) + show_indicator = Bool(True).tag(sync=True) + show_wavelength = Bool(True).tag(sync=True) def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) @@ -51,6 +70,11 @@ def __init__(self, *args, **kwargs): self.session.hub.subscribe(self, AddDataMessage, handler=self._on_data_added) + @property + def user_api(self): + return PluginUserApi(self, expose=('slice', 'wavelength', + 'show_indicator', 'show_wavelength')) + def _watch_viewer(self, viewer, watch=True): if isinstance(viewer, BqplotImageView): if watch and viewer not in self._watched_viewers: @@ -96,13 +120,13 @@ def _update_data(self, x_all): if self.wavelength == -1: if len(x_all): # initialize at middle of cube - self.slider = int(len(x_all)/2) + self.slice = int(len(x_all)/2) else: - # leave in the pre-init state and don't update the wavelength/slider + # leave in the pre-init state and don't update the wavelength/slice return # force wavelength to update from the current slider value - self._on_slider_updated({'new': self.slider}) + self._on_slider_updated({'new': self.slice}) def _viewer_slices_changed(self, value): # the slices attribute on the viewer state was changed, @@ -110,37 +134,38 @@ def _viewer_slices_changed(self, value): # the slider observer (_on_slider_updated) and sync across # any other applicable viewers if len(value) == 3: - self.slider = float(value[-1]) + self.slice = float(value[-1]) def _on_select_slice_message(self, msg): # NOTE: by setting the slice index, the observer (_on_slider_updated) # will sync across all viewers and update the wavelength with warnings.catch_warnings(): warnings.simplefilter('ignore', category=UnitsWarning) - self.slider = msg.slice + self.slice = msg.slice - def vue_change_wavelength(self, event): + @observe('wavelength') + def _on_wavelength_updated(self, event): # convert to float (JS handles stripping any invalid characters) try: - value = float(event) + value = float(event.get('new')) except ValueError: # do not accept changes, we'll revert via the slider # since this @change event doesn't have access to # the old value, and self.wavelength already updated # via the v-model - self._on_slider_updated({'new': self.slider}) + self._on_slider_updated({'new': self.slice}) return # NOTE: by setting the index, this should recursively update the # wavelength to the nearest applicable value in _on_slider_updated - self.slider = int(np.argmin(abs(value - self._x_all))) + self.slice = int(np.argmin(abs(value - self._x_all))) - @observe('setting_show_indicator', 'setting_show_wavelength') + @observe('show_indicator', 'show_wavelength') def _on_setting_changed(self, event): msg = SliceToolStateMessage({event['name']: event['new']}, sender=self) self.session.hub.broadcast(msg) - @observe('slider') + @observe('slice') def _on_slider_updated(self, event): value = int(event.get('new', int(len(self._x_all)/2))) diff --git a/jdaviz/configs/cubeviz/plugins/slice/slice.vue b/jdaviz/configs/cubeviz/plugins/slice/slice.vue index 7735dbefe6..7824378d1f 100644 --- a/jdaviz/configs/cubeviz/plugins/slice/slice.vue +++ b/jdaviz/configs/cubeviz/plugins/slice/slice.vue @@ -15,7 +15,7 @@ @@ -23,7 +23,7 @@ @@ -34,7 +34,7 @@ @@ -79,7 +78,7 @@ module.exports = { created() { this.throttledSetValue = _.throttle( - (v) => { this.slider = v; }, + (v) => { this.slice = v; }, this.wait); }, } diff --git a/jdaviz/configs/cubeviz/plugins/slice/tests/test_slice.py b/jdaviz/configs/cubeviz/plugins/slice/tests/test_slice.py index 195f3f9713..f946a246a2 100644 --- a/jdaviz/configs/cubeviz/plugins/slice/tests/test_slice.py +++ b/jdaviz/configs/cubeviz/plugins/slice/tests/test_slice.py @@ -13,9 +13,9 @@ def test_slice(cubeviz_helper, spectrum1d_cube): app.add_data_to_viewer("flux-viewer", "test") # sample cube only has 2 slices with wavelengths [4.62280007e-07 4.62360028e-07] m - assert sl.slider == 1 + assert sl.slice == 1 cubeviz_helper.select_slice(0) - assert sl.slider == 0 + assert sl.slice == 0 with pytest.raises( TypeError, @@ -28,17 +28,17 @@ def test_slice(cubeviz_helper, spectrum1d_cube): cubeviz_helper.select_slice(-5) cubeviz_helper.select_wavelength(4.62360028e-07) - assert sl.slider == 1 + assert sl.slice == 1 # from the widget this logic is duplicated (to avoid sending logic through messages) - sl.vue_change_wavelength('4.62e-07') - assert sl.slider == 0 + sl._on_wavelength_updated({'new': '4.62e-07'}) + assert sl.slice == 0 assert np.allclose(sl.wavelength, 4.62280007e-07) # make sure that passing an invalid value from the UI would revert to the previous value # JS strips invalid characters, but doesn't ensure its float-compatible - sl.vue_change_wavelength('1.2.3') - assert sl.slider == 0 + sl._on_wavelength_updated({'new': '1.2.3'}) + assert sl.slice == 0 # there is only one watched viewer, since the uncertainty/mask viewers are empty assert len(sl._watched_viewers) == 1 @@ -60,7 +60,7 @@ def test_slice(cubeviz_helper, spectrum1d_cube): new_len = len(sv.native_marks[0].x) assert new_len == 2*orig_len cubeviz_helper.select_wavelength(4.62360028e-07) - assert sl.slider == 1 + assert sl.slice == 1 @pytest.mark.filterwarnings('ignore:No observer defined on WCS') @@ -73,13 +73,13 @@ def test_indicator_settings(cubeviz_helper, spectrum1d_cube): sv = app.get_viewer('spectrum-viewer') indicator = sv.slice_indicator - assert sl.setting_show_indicator is True + assert sl.show_indicator is True assert indicator._show_if_inactive is True - assert sl.setting_show_wavelength is True + assert sl.show_wavelength is True assert indicator.label.visible is True - sl.setting_show_indicator = False + sl.show_indicator = False assert indicator._show_if_inactive is False - sl.setting_show_wavelength = False + sl.show_wavelength = False assert indicator.label.visible is False diff --git a/jdaviz/core/marks.py b/jdaviz/core/marks.py index bae9166d2b..85677e5c04 100644 --- a/jdaviz/core/marks.py +++ b/jdaviz/core/marks.py @@ -277,9 +277,9 @@ def _on_change_state(self, msg): for k, v in changes.items(): if k == 'active': self._active = v - elif k == 'setting_show_indicator': + elif k == 'show_indicator': self._show_if_inactive = v - elif k == 'setting_show_wavelength': + elif k == 'show_wavelength': self._show_wavelength = v self._update_colors_opacities()