Skip to content

Commit 3277d4d

Browse files
committed
store ephemeris information in viewer instead of relying on name
1 parent f9e1d8f commit 3277d4d

File tree

5 files changed

+91
-81
lines changed

5 files changed

+91
-81
lines changed

lcviz/helper.py

+25
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,31 @@ def get_data(self, data_label=None, cls=LightCurve, subset=None):
152152
"""
153153
return super()._get_data(data_label=data_label, mask_subset=subset, cls=cls)
154154

155+
@property
156+
def _tray_tools(self):
157+
"""
158+
Access API objects for plugins in the app toolbar.
159+
160+
Returns
161+
-------
162+
plugins : dict
163+
dict of plugin objects
164+
"""
165+
# TODO: provide user-friendly labels, user API, and move upstream to be public
166+
# for now this is just useful for dev-debugging access to toolbar entries
167+
from ipywidgets.widgets import widget_serialization
168+
return {item['name']: widget_serialization['from_json'](item['widget'], None)
169+
for item in self.app.state.tool_items}
170+
171+
def _get_clone_viewer_reference(self, reference):
172+
base_name = reference.split("[")[0]
173+
name = base_name
174+
ind = 0
175+
while name in self.viewers.keys():
176+
ind += 1
177+
name = f"{base_name}[{ind}]"
178+
return name
179+
155180
def _phase_comp_lbl(self, component):
156181
return f'phase:{component}'
157182

lcviz/parsers.py

+6-4
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
from jdaviz.core.registries import data_parser_registry
44
import lightkurve
55

6+
from lcviz.viewers import PhaseScatterView
7+
68
__all__ = ["light_curve_parser"]
79

810

@@ -45,10 +47,10 @@ def light_curve_parser(app, file_obj, data_label=None, show_in_viewer=True, **kw
4547
app.add_data_to_viewer(time_viewer_reference_name, new_data_label)
4648

4749
# add to any known phase viewers
48-
ephem_plugin = app._jdaviz_helper.plugins.get('Ephemeris', None)
49-
if ephem_plugin is not None:
50-
for viewer_id in ephem_plugin._obj.phase_viewer_ids:
51-
app.add_data_to_viewer(viewer_id, new_data_label)
50+
for viewer_id, viewer in app._viewer_store.items():
51+
if not isinstance(viewer, PhaseScatterView):
52+
continue
53+
app.add_data_to_viewer(viewer_id, new_data_label)
5254

5355

5456
def _data_with_reftime(app, light_curve):

lcviz/plugins/ephemeris/ephemeris.py

+42-37
Original file line numberDiff line numberDiff line change
@@ -143,20 +143,17 @@ def _phase_comp_lbl(self, component=None):
143143
def phase_comp_lbl(self):
144144
return self._phase_comp_lbl()
145145

146-
def _phase_viewer_id(self, component=None):
146+
def _generate_phase_viewer_id(self, component=None):
147147
if component is None:
148148
component = self.component_selected
149-
return f'flux-vs-phase:{component}'
149+
return self.app._jdaviz_helper._get_clone_viewer_reference(f'flux-vs-phase:{component}')
150150

151-
@property
152-
def phase_viewer_ids(self):
153-
viewer_ids = self.app.get_viewer_ids()
154-
return [self._phase_viewer_id(component) for component in self.component.choices
155-
if self._phase_viewer_id(component) in viewer_ids]
156-
157-
@property
158-
def phase_viewer_id(self):
159-
return self._phase_viewer_id()
151+
def _get_phase_viewers(self, lbl=None):
152+
if lbl is None:
153+
lbl = self.component_selected
154+
return [viewer for vid, viewer in self.app._viewer_store.items()
155+
if isinstance(viewer, PhaseScatterView)
156+
and viewer._ephemeris_component == lbl]
160157

161158
@property
162159
def default_phase_viewer(self):
@@ -166,13 +163,6 @@ def default_phase_viewer(self):
166163
# ephemeris component
167164
return self._get_phase_viewers()[0]
168165

169-
def _get_phase_viewers(self, lbl=None):
170-
if lbl is None:
171-
lbl = self.component_selected
172-
return [viewer for vid, viewer in self.app._viewer_store.items()
173-
if isinstance(viewer, PhaseScatterView)
174-
and viewer.ephemeris_component == lbl]
175-
176166
@property
177167
def ephemerides(self):
178168
return self._ephemerides
@@ -290,7 +280,8 @@ def create_phase_viewer(self, ephem_component=None):
290280
ephem_component : str, optional
291281
label of the component. If not provided or ``None``, will default to plugin value.
292282
"""
293-
phase_viewer_id = self._phase_viewer_id(ephem_component)
283+
if ephem_component is None:
284+
ephem_component = self.component_selected
294285
phase_comp_lbl = self._phase_comp_lbl(ephem_component)
295286
dc = self.app.data_collection
296287

@@ -300,26 +291,37 @@ def create_phase_viewer(self, ephem_component=None):
300291
if phase_comp_lbl not in [comp.label for comp in dc[0].components]:
301292
self.update_ephemeris() # calls _update_all_phase_arrays
302293

303-
create_phase_viewer = len(self._get_phase_viewers(ephem_component)) == 0
304-
if create_phase_viewer:
305-
# TODO: stack horizontally by default?
306-
self.app._on_new_viewer(NewViewerMessage(PhaseScatterView, data=None, sender=self.app),
307-
vid=phase_viewer_id, name=phase_viewer_id)
308-
309-
time_viewer_item = self.app._get_viewer_item(self.app._jdaviz_helper._default_time_viewer_reference_name) # noqa
310-
for data in dc:
311-
data_id = self.app._data_id_from_label(data.label)
312-
visible = time_viewer_item['selected_data_items'].get(data_id, 'hidden')
313-
self.app.set_data_visibility(phase_viewer_id, data.label, visible == 'visible')
294+
phase_viewer_id = self._generate_phase_viewer_id(ephem_component)
295+
# TODO: stack horizontally by default?
296+
self.app._on_new_viewer(NewViewerMessage(PhaseScatterView, data=None, sender=self.app),
297+
vid=phase_viewer_id, name=phase_viewer_id)
314298

299+
# access new viewer, set bookkeeping for ephemeris component
315300
pv = self.app.get_viewer(phase_viewer_id)
316-
if create_phase_viewer:
317-
pv.state.x_min, pv.state.x_max = (self.wrap_at-1, self.wrap_at)
318-
pv.state.x_att = self.app._jdaviz_helper._component_ids[phase_comp_lbl]
301+
pv._ephemeris_component = ephem_component
302+
# since we couldn't set ephemeris_component right away, _check_if_phase_viewer_exists
303+
# might be out-of-date
304+
self._check_if_phase_viewer_exists()
305+
306+
# set default data visibility
307+
time_viewer_item = self.app._get_viewer_item(self.app._jdaviz_helper._default_time_viewer_reference_name) # noqa
308+
for data in dc:
309+
data_id = self.app._data_id_from_label(data.label)
310+
visible = time_viewer_item['selected_data_items'].get(data_id, 'hidden')
311+
self.app.set_data_visibility(phase_viewer_id, data.label, visible == 'visible')
312+
313+
# set x_att
314+
phase_comp = self.app._jdaviz_helper._component_ids[phase_comp_lbl]
315+
pv.state.x_att = phase_comp
316+
317+
# set viewer limits
318+
pv.state.x_min, pv.state.x_max = (self.wrap_at-1, self.wrap_at)
319+
319320
return pv.user_api
320321

321322
def vue_create_phase_viewer(self, *args):
322-
self.create_phase_viewer()
323+
if not self.phase_viewer_exists:
324+
self.create_phase_viewer()
323325

324326
def vue_period_halve(self, *args):
325327
self.period /= 2
@@ -350,6 +352,7 @@ def _on_component_rename(self, old_lbl, new_lbl):
350352
viewer._ref_or_id.replace(old_lbl, new_lbl),
351353
update_id=True
352354
)
355+
viewer._ephemeris_component = new_lbl
353356

354357
# update metadata entries so that they can be used for filtering applicable entries in
355358
# data menus
@@ -465,7 +468,8 @@ def round_to_1(x):
465468
return round(x, -int(np.floor(np.log10(abs(x)))))
466469

467470
# if phase-viewer doesn't yet exist in the app, create it now
468-
self.create_phase_viewer()
471+
if not self.phase_viewer_exists:
472+
self.create_phase_viewer()
469473

470474
# update value in the dictionary (to support multi-ephems)
471475
if event:
@@ -478,9 +482,10 @@ def round_to_1(x):
478482
if event.get('name') == 'wrap_at':
479483
old = event.get('old') if event.get('old') != '' else self._prev_wrap_at
480484
if event.get('new') != '':
481-
pvs = self.default_phase_viewer.state
482485
delta_phase = event.get('new') - old
483-
pvs.x_min, pvs.x_max = pvs.x_min + delta_phase, pvs.x_max + delta_phase
486+
for pv in self._get_phase_viewers():
487+
pvs = pv.state
488+
pvs.x_min, pvs.x_max = pvs.x_min + delta_phase, pvs.x_max + delta_phase
484489
# we need to cache the old value since it could become a string
485490
# if the widget is cleared
486491
self._prev_wrap_at = event.get('new')
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
from jdaviz.configs.default.plugins import ViewerCreator
22
from jdaviz.core.registries import tool_registry, viewer_registry
33
from lcviz.events import EphemerisComponentChangedMessage
4-
from lcviz.viewers import ephem_component_from_phase_viewer_name
54

65
__all__ = ['ViewerCreator']
76

@@ -21,33 +20,23 @@ def _rebuild_available_viewers(self, *args):
2120
# and label (what appears in dropdown and the default label of the viewer)
2221

2322
if self.app._jdaviz_helper is not None:
24-
phase_viewers = [{'name': 'lcviz-phase-viewer', 'label': f'flux-vs-phase:{e}'}
23+
phase_viewers = [{'name': f'lcviz-phase-viewer:{e}', 'label': f'flux-vs-phase:{e}'}
2524
for e in self.app._jdaviz_helper.plugins['Ephemeris'].component.choices] # noqa
2625
else:
2726
phase_viewers = []
2827

2928
self.viewer_types = [v for v in self.viewer_types if v['name'].startswith('lcviz')
30-
and v['label'] != 'flux-vs-phase'] + phase_viewers
29+
and not v['label'].startswith('flux-vs-phase')] + phase_viewers
30+
self.send_state('viewer_types')
3131

3232
def vue_create_viewer(self, name):
33-
for viewer_item in self.viewer_types:
34-
if viewer_item['name'] == name:
35-
label = viewer_item['label']
36-
break
37-
else:
38-
label = viewer_registry.members[name]['label']
39-
40-
if label in self.app._jdaviz_helper.viewers:
41-
# clone whenever possible
42-
# TODO: update this to not rely directly on the label for phase-viewers, but rather
43-
# checking for the same ephemeris
44-
self.app._jdaviz_helper.viewers[label]._obj.clone_viewer()
45-
return
46-
47-
if name == 'lcviz-phase-viewer':
48-
ephem_comp = ephem_component_from_phase_viewer_name(label)
33+
if name.startswith('lcviz-phase-viewer') or name.startswith('flux-vs-phase'):
34+
ephem_comp = name.split(':')[1]
4935
ephem_plg = self.app._jdaviz_helper.plugins['Ephemeris']
5036
ephem_plg.create_phase_viewer(ephem_comp)
5137
return
38+
if name == 'flux-vs-time':
39+
# allow passing label and map to the name for upstream support
40+
name = 'lcviz-time-viewer'
5241

5342
super().vue_create_viewer(name)

lcviz/viewers.py

+10-21
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,7 @@
2020
from lightkurve import LightCurve
2121

2222

23-
__all__ = ['TimeScatterView', 'PhaseScatterView', 'ephem_component_from_phase_viewer_name']
24-
25-
26-
def ephem_component_from_phase_viewer_name(label):
27-
return label.split('[')[0].split(':')[-1]
23+
__all__ = ['TimeScatterView', 'PhaseScatterView']
2824

2925

3026
@viewer_registry("lcviz-time-viewer", label="flux-vs-time")
@@ -214,17 +210,8 @@ def apply_roi(self, roi, use_current=False):
214210

215211
super().apply_roi(roi, use_current=use_current)
216212

217-
def _get_clone_viewer_reference(self):
218-
base_name = self.reference.split("[")[0]
219-
name = base_name
220-
ind = 0
221-
while name in self.jdaviz_helper.viewers.keys():
222-
ind += 1
223-
name = f"{base_name}[{ind}]"
224-
return name
225-
226213
def clone_viewer(self):
227-
name = self._get_clone_viewer_reference()
214+
name = self.jdaviz_helper._get_clone_viewer_reference(self.reference)
228215

229216
self.jdaviz_app._on_new_viewer(NewViewerMessage(self.__class__,
230217
data=None,
@@ -240,7 +227,9 @@ def clone_viewer(self):
240227
# TODO: don't revert color when adding same data to a new viewer
241228
# (same happens when creating a phase-viewer from ephemeris plugin)
242229

243-
new_viewer = self.jdaviz_helper.viewers[name]._obj
230+
new_viewer = self.jdaviz_app.get_viewer(name)
231+
if hasattr(self, 'ephemeris_component'):
232+
new_viewer._ephemeris_component = self._ephemeris_component
244233
for k, v in this_state.items():
245234
if k in ('layers',):
246235
continue
@@ -251,13 +240,13 @@ def clone_viewer(self):
251240

252241
@viewer_registry("lcviz-phase-viewer", label="flux-vs-phase")
253242
class PhaseScatterView(TimeScatterView):
254-
@property
255-
def ephemeris_component(self):
256-
return ephem_component_from_phase_viewer_name(self.reference)
243+
def __init__(self, *args, **kwargs):
244+
self._ephemeris_component = 'default'
245+
super().__init__(*args, **kwargs)
257246

258247
def _set_plot_x_axes(self, dc, component_labels, light_curve):
259248
# setting of y_att will be handled by ephemeris plugin
260-
self.state.x_att = dc[0].components[component_labels.index(f'phase:{self.ephemeris_component}')] # noqa
249+
self.state.x_att = dc[0].components[component_labels.index(f'phase:{self._ephemeris_component}')] # noqa
261250
self.figure.axes[0].label = 'phase'
262251
self.figure.axes[0].num_ticks = 5
263252

@@ -266,4 +255,4 @@ def times_to_phases(self, times):
266255
if ephem is None:
267256
raise ValueError("must have ephemeris plugin loaded to convert")
268257

269-
return ephem.times_to_phases(times, ephem_component=self.ephemeris_component)
258+
return ephem.times_to_phases(times, ephem_component=self._ephemeris_component)

0 commit comments

Comments
 (0)