Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

user API for slice plugin #1635

Merged
merged 4 commits into from
Sep 15, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion CHANGES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
^^^^^^^
Expand Down
53 changes: 39 additions & 14 deletions jdaviz/configs/cubeviz/plugins/slice/slice.py
Original file line number Diff line number Diff line change
Expand Up @@ -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 <slice>` for more details.

Only the following attributes and methods are available through the
:ref:`public plugin API <plugin-apis>`:

* :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)
Expand All @@ -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:
Expand Down Expand Up @@ -96,51 +120,52 @@ 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,
# so we'll update the slider to match which will trigger
# 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)))

Expand Down
11 changes: 5 additions & 6 deletions jdaviz/configs/cubeviz/plugins/slice/slice.vue
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,15 @@
<v-switch
label="Show Indicator"
hint="Show indicator in spectral viewer even when slice tool is inactive."
v-model="setting_show_indicator"
v-model="show_indicator"
persistent-hint>
</v-switch>
</v-row>
<v-row>
<v-switch
label="Show Wavelength"
hint="Show slice wavelength in label to right of indicator."
v-model="setting_show_wavelength"
v-model="show_wavelength"
persistent-hint>
</v-switch>
</v-row>
Expand All @@ -34,7 +34,7 @@

<v-row>
<v-slider
:value="slider"
:value="slice"
@input="throttledSetValue"
class="align-center"
:max="max_value"
Expand All @@ -46,7 +46,7 @@
<v-row class="row-no-outside-padding row-min-bottom-padding">
<v-col>
<v-text-field
v-model="slider"
v-model="slice"
class="mt-0 pt-0"
type="number"
label="Slice"
Expand All @@ -63,7 +63,6 @@
<v-text-field
v-model="wavelength"
class="mt-0 pt-0"
@change="change_wavelength"
label="Wavelength"
hint="Wavelength corresponding to slice, in units of spectrum"
></v-text-field>
Expand All @@ -79,7 +78,7 @@
module.exports = {
created() {
this.throttledSetValue = _.throttle(
(v) => { this.slider = v; },
(v) => { this.slice = v; },
this.wait);
},
}
Expand Down
24 changes: 12 additions & 12 deletions jdaviz/configs/cubeviz/plugins/slice/tests/test_slice.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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
Expand All @@ -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')
Expand All @@ -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
4 changes: 2 additions & 2 deletions jdaviz/core/marks.py
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down