From a414887cebfaa0185390e1e189dd852b8fa8aed7 Mon Sep 17 00:00:00 2001 From: Kyle Conroy Date: Mon, 13 Nov 2023 12:20:08 -0500 Subject: [PATCH] viewer user-APIs exposing zoom/limit options --- jdaviz/configs/cubeviz/plugins/viewers.py | 3 ++- jdaviz/configs/default/plugins/viewers.py | 22 ++++++++++++++++++++++ jdaviz/core/helpers.py | 13 +++++++++++++ jdaviz/core/user_api.py | 19 ++++++++++++++++++- 4 files changed, 55 insertions(+), 2 deletions(-) diff --git a/jdaviz/configs/cubeviz/plugins/viewers.py b/jdaviz/configs/cubeviz/plugins/viewers.py index e7119e8732..2c2ffb2cd9 100644 --- a/jdaviz/configs/cubeviz/plugins/viewers.py +++ b/jdaviz/configs/cubeviz/plugins/viewers.py @@ -4,6 +4,7 @@ from bqplot import Lines from glue_jupyter.bqplot.image import BqplotImageView +from jdaviz.core.astrowidgets_api import AstrowidgetsImageViewerMixin from jdaviz.core.registries import viewer_registry from jdaviz.core.marks import SliceIndicatorMarks, ShadowSpatialSpectral from jdaviz.configs.cubeviz.helper import layer_is_cube_image_data @@ -16,7 +17,7 @@ @viewer_registry("cubeviz-image-viewer", label="Image 2D (Cubeviz)") -class CubevizImageView(JdavizViewerMixin, BqplotImageView): +class CubevizImageView(JdavizViewerMixin, BqplotImageView, AstrowidgetsImageViewerMixin): # categories: zoom resets, (zoom, pan), subset, select tools, shortcuts # NOTE: zoom and pan are merged here for space consideration and to avoid # overflow to second row when opening the tray diff --git a/jdaviz/configs/default/plugins/viewers.py b/jdaviz/configs/default/plugins/viewers.py index 97c0906e03..57f4f9723c 100644 --- a/jdaviz/configs/default/plugins/viewers.py +++ b/jdaviz/configs/default/plugins/viewers.py @@ -1,4 +1,5 @@ import numpy as np +from echo import delay_callback from glue.viewers.scatter.state import ScatterLayerState as BqplotScatterLayerState from glue_jupyter.bqplot.profile import BqplotProfileView @@ -7,7 +8,9 @@ from jdaviz.configs.imviz.helper import layer_is_image_data from jdaviz.components.toolbar_nested import NestedJupyterToolbar +from jdaviz.core.astrowidgets_api import AstrowidgetsImageViewerMixin from jdaviz.core.registries import viewer_registry +from jdaviz.core.user_api import ViewerUserApi from jdaviz.utils import ColorCycler, get_subset_type __all__ = ['JdavizViewerMixin'] @@ -30,6 +33,25 @@ def __init__(self, *args, **kwargs): # Allow each viewer to cycle through colors for each new addition to the viewer: self.color_cycler = ColorCycler() + @property + def user_api(self): + if isinstance(self, AstrowidgetsImageViewerMixin): + expose = ['zoom', 'zoom_level'] + else: + expose = ['set_lims'] + return ViewerUserApi(self, expose=expose) + + def set_lims(self, x_min=None, x_max=None, y_min=None, y_max=None): + with delay_callback(self.state, 'x_min', 'x_max', 'y_min', 'y_max'): + if x_min is not None: + self.state.x_min = x_min + if x_max is not None: + self.state.x_max = x_max + if y_min is not None: + self.state.y_min = y_min + if y_max is not None: + self.state.y_max = y_max + @property def native_marks(self): """ diff --git a/jdaviz/core/helpers.py b/jdaviz/core/helpers.py index faafa0afd0..a0fb9a2b20 100644 --- a/jdaviz/core/helpers.py +++ b/jdaviz/core/helpers.py @@ -122,6 +122,19 @@ def plugins(self): return {item['label']: widget_serialization['from_json'](item['widget'], None).user_api for item in self.app.state.tray_items} + @property + def viewers(self): + """ + Access API objects for any viewer. + + Returns + ------- + viewers : dict + dict of viewer objects + """ + return {getattr(viewer, 'reference', k): viewer.user_api + for k, viewer in self.app._viewer_store.items()} + @property def fitted_models(self): """ diff --git a/jdaviz/core/user_api.py b/jdaviz/core/user_api.py index ae96681651..01163406d9 100644 --- a/jdaviz/core/user_api.py +++ b/jdaviz/core/user_api.py @@ -1,6 +1,6 @@ import astropy.units as u -__all__ = ['UserApiWrapper', 'PluginUserApi'] +__all__ = ['UserApiWrapper', 'PluginUserApi', 'ViewerUserApi'] _internal_attrs = ('_obj', '_expose', '_readonly', '__doc__') @@ -96,3 +96,20 @@ def __init__(self, plugin, expose=[], readonly=[]): def __repr__(self): return f'<{self._obj._registry_label} API>' + + +class ViewerUserApi(UserApiWrapper): + """ + This is an API wrapper around a viewer. For a full list of attributes/methods, + call dir(viewer_object) and for help on any of those methods, + call help(viewer_object.attribute). + + For example:: + help(viewer_object.show) + """ + def __init__(self, viewer, expose=[], readonly=[]): + expose = list(set(list(expose) + [])) + super().__init__(viewer, expose, readonly) + + def __repr__(self): + return f'<{self._obj.reference} API>'