diff --git a/jdaviz/configs/imviz/plugins/coords_info/coords_info.py b/jdaviz/configs/imviz/plugins/coords_info/coords_info.py index b90d22a38d..032c0c0271 100644 --- a/jdaviz/configs/imviz/plugins/coords_info/coords_info.py +++ b/jdaviz/configs/imviz/plugins/coords_info/coords_info.py @@ -1,7 +1,9 @@ from traitlets import Bool, Unicode +from jdaviz.configs.specviz.plugins.viewers import SpecvizProfileView from jdaviz.core.registries import tool_registry from jdaviz.core.template_mixin import TemplateMixin +from jdaviz.core.marks import PluginScatter __all__ = ['CoordsInfo'] @@ -23,6 +25,27 @@ class CoordsInfo(TemplateMixin): unreliable_world = Bool(False).tag(sync=True) unreliable_pixel = Bool(False).tag(sync=True) + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self._marks = {} + + @property + def marks(self): + """ + Access the marks created by this plugin. + """ + if self._marks: + # TODO: replace with cache property? + return self._marks + + # create marks for each of the spectral viewers (will need a listener event to create marks + # for new viewers if dynamic creation of spectral viewers is ever supported) + for id, viewer in self.app._viewer_store.items(): + if isinstance(viewer, SpecvizProfileView): + self._marks[id] = PluginScatter(viewer, visible=False) + viewer.figure.marks = viewer.figure.marks + [self._marks[id]] + return self._marks + def reset_coords_display(self): self.world_label_prefix = '\u00A0' self.world_label_prefix_2 = '\u00A0' diff --git a/jdaviz/configs/specviz/plugins/viewers.py b/jdaviz/configs/specviz/plugins/viewers.py index f6868212d6..7606cfa262 100644 --- a/jdaviz/configs/specviz/plugins/viewers.py +++ b/jdaviz/configs/specviz/plugins/viewers.py @@ -128,24 +128,37 @@ def on_mouse_or_key_event(self, data): self.label_mouseover.pixel = "" self.label_mouseover.reset_coords_display() self.label_mouseover.value = "" + self.label_mouseover.marks[self._reference_id].visible = False return - fmt = 'x={:0' + str(closest_maxsize) + '.1f}' - self.label_mouseover.pixel = fmt.format(closest_i) - self.label_mouseover.world_label_prefix = 'Wave' - self.label_mouseover.world_ra = f'{closest_wave.value:10.5e}' - self.label_mouseover.world_dec = closest_wave.unit.to_string() - self.label_mouseover.world_label_prefix_2 = 'Flux' - self.label_mouseover.world_ra_deg = f'{closest_flux.value:10.5e}' - self.label_mouseover.world_dec_deg = closest_flux.unit.to_string() - self.label_mouseover.icon = closest_label - self.label_mouseover.value = "" # Not used + # show the locked marker/coords only if either no tool or the default tool is active + locking_active = self.toolbar.active_tool_id in self.toolbar.default_tool_priority + [None] # noqa + if locking_active: + fmt = 'x={:0' + str(closest_maxsize) + '.1f}' + self.label_mouseover.pixel = fmt.format(closest_i) + self.label_mouseover.world_label_prefix = 'Wave' + self.label_mouseover.world_ra = f'{closest_wave.value:10.5e}' + self.label_mouseover.world_dec = closest_wave.unit.to_string() + self.label_mouseover.world_label_prefix_2 = 'Flux' + self.label_mouseover.world_ra_deg = f'{closest_flux.value:10.5e}' + self.label_mouseover.world_dec_deg = closest_flux.unit.to_string() + self.label_mouseover.icon = closest_label + self.label_mouseover.value = "" # Not used + self.label_mouseover.marks[self._reference_id].update_xy([closest_wave.value], [closest_flux.value]) # noqa + self.label_mouseover.marks[self._reference_id].visible = True + else: + # show exact plot coordinates (useful for drawing spectral subsets or zoom ranges) + fmt = 'x={:+10.5e} y={:+10.5e}' + self.label_mouseover.icon = "" + self.label_mouseover.pixel = fmt.format(x, y) + self.label_mouseover.marks[self._reference_id].visible = False elif data['event'] == 'mouseleave' or data['event'] == 'mouseenter': self.label_mouseover.icon = "" self.label_mouseover.pixel = "" self.label_mouseover.reset_coords_display() self.label_mouseover.value = "" + self.label_mouseover.marks[self._reference_id].visible = False def _expected_subset_layer_default(self, layer_state): super()._expected_subset_layer_default(layer_state) diff --git a/jdaviz/core/marks.py b/jdaviz/core/marks.py index 110cc87deb..cdfa05f7d7 100644 --- a/jdaviz/core/marks.py +++ b/jdaviz/core/marks.py @@ -13,7 +13,7 @@ __all__ = ['OffscreenLinesMarks', 'BaseSpectrumVerticalLine', 'SpectralLine', 'SliceIndicatorMarks', 'ShadowMixin', 'ShadowLine', 'ShadowLabelFixedY', - 'PluginLine', + 'PluginLine', 'PluginScatter', 'LineAnalysisContinuum', 'LineAnalysisContinuumCenter', 'LineAnalysisContinuumLeft', 'LineAnalysisContinuumRight', 'LineUncertainties', 'ScatterMask', 'SelectedSpaxel'] @@ -484,19 +484,31 @@ def _on_shadowing_changed(self, change): self._update_align() -class PluginLine(Lines, HubListener): - def __init__(self, viewer, x=[], y=[], **kwargs): - # color is same blue as import button - super().__init__(x=x, y=y, colors=["#007BA1"], scales=viewer.scales, **kwargs) - +class PluginMark(): def update_xy(self, x, y): self.x = np.asarray(x) self.y = np.asarray(y) + def append_xy(self, x, y): + self.x = np.append(self.x, x) + self.y = np.append(self.y, y) + def clear(self): self.update_xy([], []) +class PluginLine(Lines, PluginMark, HubListener): + def __init__(self, viewer, x=[], y=[], **kwargs): + # color is same blue as import button + super().__init__(x=x, y=y, colors=["#007BA1"], scales=viewer.scales, **kwargs) + + +class PluginScatter(Scatter, PluginMark, HubListener): + def __init__(self, viewer, x=[], y=[], **kwargs): + # color is same blue as import button + super().__init__(x=x, y=y, colors=["#007BA1"], scales=viewer.scales, **kwargs) + + class LineAnalysisContinuum(PluginLine): pass