Skip to content

Commit

Permalink
Merge branch 'main' into feature-tpf
Browse files Browse the repository at this point in the history
  • Loading branch information
kecnry committed Mar 11, 2024
2 parents a81dea6 + 7b78df5 commit ab1019c
Show file tree
Hide file tree
Showing 19 changed files with 319 additions and 119 deletions.
9 changes: 7 additions & 2 deletions CHANGES.rst
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
0.2.0 - unreleased
0.3.0 - unreleased
------------------

* Clone viewer tool. [#74]
* Ability to create additional viewers. [#94]

0.2.0 (02-26-2024)
------------------

* Clone viewer tool. [#74, #91]

* Flux column plugin to choose which column is treated as the flux column for each dataset. [#77]

Expand Down
45 changes: 36 additions & 9 deletions lcviz/helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,10 @@
from glue.core.component_id import ComponentID
from glue.core.link_helpers import LinkSame
from jdaviz.core.helpers import ConfigHelper
from lcviz.viewers import TimeScatterView

__all__ = ['LCviz']

_default_time_viewer_reference_name = 'flux-vs-time'

custom_components = {'plugin-ephemeris-select': 'components/plugin_ephemeris_select.vue'}

# Register pure vue component. This allows us to do recursive component instantiation only in the
Expand All @@ -22,11 +21,7 @@


def _get_range_subset_bounds(self, subset_state, *args, **kwargs):
# Instead of overriding the jdaviz version of this method on jdaviz.Application,
# we could put in jdaviz by (1) checking if helper has a
# _default_time_viewer_reference_name, (2) using the LCviz version if so, and (3)
# using the jdaviz version otherwise.
viewer = self.get_viewer(self._jdaviz_helper._default_time_viewer_reference_name)
viewer = self._jdaviz_helper.default_time_viewer._obj
light_curve = viewer.data()[0]
reference_time = light_curve.meta['reference_time']
if viewer:
Expand Down Expand Up @@ -71,7 +66,7 @@ class LCviz(ConfigHelper):
'tab_headers': True},
'dense_toolbar': False,
'context': {'notebook': {'max_height': '600px'}}},
'toolbar': ['g-data-tools', 'g-subset-tools', 'lcviz-coords-info'],
'toolbar': ['g-data-tools', 'g-subset-tools', 'lcviz-viewer-creator', 'lcviz-coords-info'],
'tray': ['lcviz-metadata-viewer', 'flux-column',
'lcviz-plot-options', 'lcviz-subset-plugin',
'lcviz-markers', 'time-selector',
Expand All @@ -87,7 +82,6 @@ class LCviz(ConfigHelper):

def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self._default_time_viewer_reference_name = _default_time_viewer_reference_name

# override jdaviz behavior to support temporal subsets
self.app._get_range_subset_bounds = (
Expand Down Expand Up @@ -153,6 +147,39 @@ def get_data(self, data_label=None, cls=LightCurve, subset=None):
"""
return super()._get_data(data_label=data_label, mask_subset=subset, cls=cls)

@property
def default_time_viewer(self):
tvs = [viewer for vid, viewer in self.app._viewer_store.items()
if isinstance(viewer, TimeScatterView)]
if not len(tvs):
raise ValueError("no time viewers exist")
return tvs[0].user_api

@property
def _tray_tools(self):
"""
Access API objects for plugins in the app toolbar.
Returns
-------
plugins : dict
dict of plugin objects
"""
# TODO: provide user-friendly labels, user API, and move upstream to be public
# for now this is just useful for dev-debugging access to toolbar entries
from ipywidgets.widgets import widget_serialization
return {item['name']: widget_serialization['from_json'](item['widget'], None)
for item in self.app.state.tool_items}

def _get_clone_viewer_reference(self, reference):
base_name = reference.split("[")[0]
name = base_name
ind = 0
while name in self.viewers.keys():
ind += 1
name = f"{base_name}[{ind}]"
return name

def _phase_comp_lbl(self, component):
return f'phase:{component}'

Expand Down
26 changes: 16 additions & 10 deletions lcviz/parsers.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,13 @@
from jdaviz.core.registries import data_parser_registry
import lightkurve

from lcviz.viewers import PhaseScatterView, TimeScatterView

__all__ = ["light_curve_parser"]


@data_parser_registry("light_curve_parser")
def light_curve_parser(app, file_obj, data_label=None, show_in_viewer=True, **kwargs):
time_viewer_reference_name = app._jdaviz_helper._default_time_viewer_reference_name

# load a LightCurve or TargetPixelFile object:
cls_with_translator = (
lightkurve.LightCurve,
Expand Down Expand Up @@ -57,22 +57,28 @@ def light_curve_parser(app, file_obj, data_label=None, show_in_viewer=True, **kw
# TODO: move this to an event listener on add_data so that we can also remove when empty?
from jdaviz.core.events import NewViewerMessage
from lcviz.viewers import CubeView
viewer_reference_name = 'image'
if viewer_reference_name not in app._viewer_store.keys():
app._on_new_viewer(NewViewerMessage(CubeView, data=None, sender=app),
vid='image', name='image')
if show_in_viewer:
app.add_data_to_viewer(viewer_reference_name, new_data_label)
found_viewer = False
for viewer_id, viewer in app._viewer_store.items():
if isinstance(viewer, CubeView):
app.add_data_to_viewer(viewer_id, new_data_label)
found_viewer = True
if not found_viewer:
app._on_new_viewer(NewViewerMessage(CubeView, data=None, sender=app),
vid='image', name='image')
app.add_data_to_viewer('image', new_data_label)

else:
if show_in_viewer:
app.add_data_to_viewer(time_viewer_reference_name, new_data_label)
for viewer_id, viewer in app._viewer_store.items():
if isinstance(viewer, (TimeScatterView, PhaseScatterView)):
app.add_data_to_viewer(viewer_id, new_data_label)

# add to any known phase viewers
ephem_plugin = app._jdaviz_helper.plugins.get('Ephemeris', None)
if ephem_plugin is not None:
for viewer_id in ephem_plugin._obj.phase_viewer_ids:
app.add_data_to_viewer(viewer_id, new_data_label)
for viewer in ephem_plugin._obj._get_phase_viewers():
app.add_data_to_viewer(viewer.reference, new_data_label)


def _data_with_reftime(app, light_curve):
Expand Down
4 changes: 3 additions & 1 deletion lcviz/plugins/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
from .binning.binning import * # noqa
from .viewer_creator.viewer_creator import * # noqa
from .coords_info.coords_info import * # noqa

from .binning.binning import * # noqa
from .ephemeris.ephemeris import * # noqa
from .export_plot.export_plot import * # noqa
from .flatten.flatten import * # noqa
Expand Down
34 changes: 21 additions & 13 deletions lcviz/plugins/binning/binning.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@

from lcviz.components import FluxColumnSelectMixin
from lcviz.events import EphemerisChangedMessage
from lcviz.helper import _default_time_viewer_reference_name
from lcviz.marks import LivePreviewBinning
from lcviz.parsers import _data_with_reftime
from lcviz.viewers import TimeScatterView, PhaseScatterView
from lcviz.components import EphemerisSelectMixin


Expand Down Expand Up @@ -68,7 +68,8 @@ def not_from_binning_plugin(data):
return data.meta.get('Plugin', None) != self.__class__.__name__
self.dataset.add_filter(not_from_binning_plugin)

self.hub.subscribe(self, ViewerAddedMessage, handler=self._set_results_viewer)
# TODO: viewer added also needs to repopulate marks
self.hub.subscribe(self, ViewerAddedMessage, handler=self._on_add_viewer)
self.hub.subscribe(self, ViewerRemovedMessage, handler=self._set_results_viewer)
self.hub.subscribe(self, EphemerisChangedMessage, handler=self._on_ephemeris_update)

Expand All @@ -95,15 +96,15 @@ def input_lc(self):
@property
def marks(self):
marks = {}
for id, viewer in self.app._viewer_store.items():
for viewer in self.app._viewer_store.values():
for mark in viewer.figure.marks:
if isinstance(mark, LivePreviewBinning):
marks[id] = mark
marks[viewer.reference] = mark
break
else:
mark = LivePreviewBinning(viewer, visible=self.is_active)
viewer.figure.marks = viewer.figure.marks + [mark]
marks[id] = mark
marks[viewer.reference] = mark
return marks

def _clear_marks(self):
Expand All @@ -129,25 +130,31 @@ def _set_results_viewer(self, event={}):

def viewer_filter(viewer):
if self.ephemeris_selected in self.ephemeris._manual_options:
return viewer.reference == _default_time_viewer_reference_name
if 'flux-vs-phase:' not in viewer.reference:
return isinstance(viewer, TimeScatterView)
if not isinstance(viewer, PhaseScatterView):
# ephemeris selected, but no active phase viewers
return False
return viewer.reference.split('flux-vs-phase:')[1] == self.ephemeris_selected
return viewer._ephemeris_component == self.ephemeris_selected

self.add_results.viewer.filters = [viewer_filter]

def _on_add_viewer(self, msg):
self._set_results_viewer()
self._live_update()

@observe('is_active', 'show_live_preview')
def _toggle_marks(self, event={}):
visible = self.show_live_preview and self.is_active

for viewer_id, mark in self.marks.items():
for viewer_ref, mark in self.marks.items():
if not visible:
this_visible = False
elif self.ephemeris_selected == 'No ephemeris':
this_visible = True
else:
this_visible = viewer_id.split(':')[-1] == self.ephemeris_selected
viewer = self.app.get_viewer(viewer_ref)
viewer_ephem = getattr(viewer, '_ephemeris_component', None)
this_visible = viewer_ephem == self.ephemeris_selected

mark.visible = this_visible

Expand Down Expand Up @@ -260,10 +267,11 @@ def bin(self, add_data=True):

if self.ephemeris_selected != 'No ephemeris':
# prevent phase axis from becoming a time axis:
viewer_id = self.ephemeris_plugin._obj.phase_viewer_id
pv = self.app.get_viewer(viewer_id)
ephemeris_plugin = self.app._jdaviz_helper.plugins['Ephemeris']
phase_comp_lbl = self.app._jdaviz_helper._phase_comp_lbl(self.ephemeris_selected)
pv.state.x_att = self.app._jdaviz_helper._component_ids[phase_comp_lbl]
phase_comp = self.app._jdaviz_helper._component_ids[phase_comp_lbl]
for pv in ephemeris_plugin._obj._get_phase_viewers(self.ephemeris_selected):
pv.state.x_att = phase_comp
# by resetting x_att, the preview marks may have dissappeared
self._live_update()

Expand Down
Loading

0 comments on commit ab1019c

Please sign in to comment.