Source code for jdaviz.configs.default.plugins.plot_options.plot_options

import os
import numpy as np

from astropy.visualization import PercentileInterval
from traitlets import Any, Dict, Float, Bool, Int, List, Unicode, observe

from glue.viewers.scatter.state import ScatterViewerState
from glue.viewers.profile.state import ProfileViewerState, ProfileLayerState
from glue.viewers.image.state import ImageSubsetLayerState
from glue_jupyter.bqplot.scatter.layer_artist import BqplotScatterLayerState
from glue_jupyter.bqplot.image.state import BqplotImageLayerState
from glue_jupyter.common.toolbar_vuetify import read_icon

from jdaviz.core.registries import tray_registry
from jdaviz.core.template_mixin import (PluginTemplateMixin, ViewerSelect, LayerSelect,
                                        PlotOptionsSyncState, Plot)
from jdaviz.core.user_api import PluginUserApi
from jdaviz.core.tools import ICON_DIR
from jdaviz.core.custom_traitlets import IntHandleEmpty

__all__ = ['PlotOptions']


[docs]@tray_registry('g-plot-options', label="Plot Options") class PlotOptions(PluginTemplateMixin): """ The Plot Options Plugin gives access to per-viewer and per-layer options and enables setting across multiple viewers/layers simultaneously. 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` * ``multiselect``: whether ``viewer`` and ``layer`` should both be in multiselect mode. * ``viewer`` (:class:`~jdaviz.core.template_mixin.ViewerSelect`): * ``layer`` (:class:`~jdaviz.core.template_mixin.LayerSelect`): * :meth:`select_all` * ``subset_visible`` (:class:`~jdaviz.core.template_mixin.PlotOptionsSyncState`): whether a subset should be visible. * ``subset_color`` (:class:`~jdaviz.core.template_mixin.PlotOptionsSyncState`): not exposed for Specviz * ``axes_visible`` (:class:`~jdaviz.core.template_mixin.PlotOptionsSyncState`): not exposed for Imviz * ``collapse_function`` (:class:`~jdaviz.core.template_mixin.PlotOptionsSyncState`): only exposed for Cubeviz * ``line_visible`` (:class:`~jdaviz.core.template_mixin.PlotOptionsSyncState`): not exposed for Imviz * ``line_color`` (:class:`~jdaviz.core.template_mixin.PlotOptionsSyncState`): not exposed for Imviz * ``line_width`` (:class:`~jdaviz.core.template_mixin.PlotOptionsSyncState`): not exposed for Imviz * ``line_opacity`` (:class:`~jdaviz.core.template_mixin.PlotOptionsSyncState`): not exposed for Imviz * ``line_as_steps`` (:class:`~jdaviz.core.template_mixin.PlotOptionsSyncState`): not exposed for Imviz * ``uncertainty_visible`` (:class:`~jdaviz.core.template_mixin.PlotOptionsSyncState`): not exposed for Imviz * ``stretch_function`` (:class:`~jdaviz.core.template_mixin.PlotOptionsSyncState`): not exposed for Specviz * ``stretch_preset`` (:class:`~jdaviz.core.template_mixin.PlotOptionsSyncState`): not exposed for Specviz * ``stretch_vmin`` (:class:`~jdaviz.core.template_mixin.PlotOptionsSyncState`): not exposed for Specviz * ``stretch_vmax`` (:class:`~jdaviz.core.template_mixin.PlotOptionsSyncState`): not exposed for Specviz * ``stretch_hist_zoom_limits`` : whether to show the histogram for the current zoom limits instead of all data within the layer; not exposed for Specviz. * ``stretch_hist_nbins`` : number of bins to use in creating the histogram; not exposed for Specviz. * ``image_visible`` (:class:`~jdaviz.core.template_mixin.PlotOptionsSyncState`): whether the image bitmap is visible; not exposed for Specviz. * ``image_color_mode`` (:class:`~jdaviz.core.template_mixin.PlotOptionsSyncState`): not exposed for Specviz * ``image_color`` (:class:`~jdaviz.core.template_mixin.PlotOptionsSyncState`): not exposed for Specviz. This only applies when ``image_color_mode`` is "Monochromatic". * ``image_colormap`` (:class:`~jdaviz.core.template_mixin.PlotOptionsSyncState`): not exposed for Specviz. This only applies when ``image_color_mode`` is "Colormap". * ``image_opacity`` (:class:`~jdaviz.core.template_mixin.PlotOptionsSyncState`): not exposed for Specviz. Valid values are between 0 and 1, inclusive. Default is 1. * ``image_contrast`` (:class:`~jdaviz.core.template_mixin.PlotOptionsSyncState`): not exposed for Specviz. Valid values are between 0 and 4, inclusive. Default is 1. * ``image_bias`` (:class:`~jdaviz.core.template_mixin.PlotOptionsSyncState`): not exposed for Specviz. Valid values are between 0 and 1, inclusive. Default is 0.5. * ``contour_visible`` (:class:`~jdaviz.core.template_mixin.PlotOptionsSyncState`): whether the contour is visible; not exposed for Specviz * ``contour_mode`` (:class:`~jdaviz.core.template_mixin.PlotOptionsSyncState`): not exposed for Specviz * ``contour_min`` (:class:`~jdaviz.core.template_mixin.PlotOptionsSyncState`): not exposed for Specviz. This only applies when ``contour_mode`` is "Linear". * ``contour_max`` (:class:`~jdaviz.core.template_mixin.PlotOptionsSyncState`): not exposed for Specviz. This only applies when ``contour_mode`` is "Linear". * ``contour_nlevels`` (:class:`~jdaviz.core.template_mixin.PlotOptionsSyncState`): not exposed for Specviz. This only applies when ``contour_mode`` is "Linear". * ``contour_custom_levels`` (:class:`~jdaviz.core.template_mixin.PlotOptionsSyncState`): not exposed for Specviz. This only applies when ``contour_mode`` is "Custom". """ template_file = __file__, "plot_options.vue" uses_active_status = Bool(True).tag(sync=True) # multiselect is shared between viewer and layer multiselect = Bool(False).tag(sync=True) viewer_items = List().tag(sync=True) viewer_selected = Any().tag(sync=True) # Any needed for multiselect layer_items = List().tag(sync=True) layer_selected = Any().tag(sync=True) # Any needed for multiselect # profile/line viewer/layer options: line_visible_value = Bool().tag(sync=True) line_visible_sync = Dict().tag(sync=True) collapse_func_value = Unicode().tag(sync=True) collapse_func_sync = Dict().tag(sync=True) line_color_value = Any().tag(sync=True) line_color_sync = Dict().tag(sync=True) line_width_value = Int().tag(sync=True) line_width_sync = Dict().tag(sync=True) line_opacity_value = Float().tag(sync=True) line_opacity_sync = Dict().tag(sync=True) line_as_steps_value = Bool().tag(sync=True) line_as_steps_sync = Dict().tag(sync=True) uncertainty_visible_value = Int().tag(sync=True) uncertainty_visible_sync = Dict().tag(sync=True) # scatter/marker options marker_visible_value = Bool().tag(sync=True) marker_visible_sync = Dict().tag(sync=True) marker_fill_value = Bool().tag(sync=True) marker_fill_sync = Dict().tag(sync=True) marker_opacity_value = Float().tag(sync=True) marker_opacity_sync = Dict().tag(sync=True) marker_size_mode_value = Unicode().tag(sync=True) marker_size_mode_sync = Dict().tag(sync=True) marker_size_value = Float().tag(sync=True) marker_size_sync = Dict().tag(sync=True) marker_size_scale_value = Float().tag(sync=True) marker_size_scale_sync = Dict().tag(sync=True) marker_size_col_value = Unicode().tag(sync=True) marker_size_col_sync = Dict().tag(sync=True) marker_size_vmin_value = Float().tag(sync=True) marker_size_vmin_sync = Dict().tag(sync=True) marker_size_vmax_value = Float().tag(sync=True) marker_size_vmax_sync = Dict().tag(sync=True) marker_color_mode_value = Unicode().tag(sync=True) marker_color_mode_sync = Dict().tag(sync=True) marker_color_value = Any().tag(sync=True) marker_color_sync = Dict().tag(sync=True) marker_color_col_value = Unicode().tag(sync=True) marker_color_col_sync = Dict().tag(sync=True) marker_colormap_value = Unicode().tag(sync=True) marker_colormap_sync = Dict().tag(sync=True) marker_colormap_vmin_value = Float().tag(sync=True) marker_colormap_vmin_sync = Dict().tag(sync=True) marker_colormap_vmax_value = Float().tag(sync=True) marker_colormap_vmax_sync = Dict().tag(sync=True) # image viewer/layer options stretch_function_value = Unicode().tag(sync=True) stretch_function_sync = Dict().tag(sync=True) stretch_preset_value = Any().tag(sync=True) # glue will pass either a float or string stretch_preset_sync = Dict().tag(sync=True) stretch_vmin_value = Float().tag(sync=True) stretch_vmin_sync = Dict().tag(sync=True) stretch_vmax_value = Float().tag(sync=True) stretch_vmax_sync = Dict().tag(sync=True) stretch_hist_zoom_limits = Bool().tag(sync=True) stretch_hist_nbins = IntHandleEmpty(25).tag(sync=True) stretch_histogram_widget = Unicode().tag(sync=True) subset_visible_value = Bool().tag(sync=True) subset_visible_sync = Dict().tag(sync=True) subset_color_value = Unicode().tag(sync=True) subset_color_sync = Dict().tag(sync=True) image_visible_value = Bool().tag(sync=True) image_visible_sync = Dict().tag(sync=True) image_color_mode_value = Unicode().tag(sync=True) image_color_mode_sync = Dict().tag(sync=True) image_color_value = Any().tag(sync=True) image_color_sync = Dict().tag(sync=True) image_colormap_value = Unicode().tag(sync=True) image_colormap_sync = Dict().tag(sync=True) image_opacity_value = Float().tag(sync=True) image_opacity_sync = Dict().tag(sync=True) image_contrast_value = Float().tag(sync=True) image_contrast_sync = Dict().tag(sync=True) image_bias_value = Float().tag(sync=True) image_bias_sync = Dict().tag(sync=True) contour_spinner = Bool().tag(sync=True) contour_visible_value = Bool().tag(sync=True) contour_visible_sync = Dict().tag(sync=True) contour_mode_value = Unicode().tag(sync=True) contour_mode_sync = Dict().tag(sync=True) contour_min_value = Float().tag(sync=True) contour_min_sync = Dict().tag(sync=True) contour_max_value = Float().tag(sync=True) contour_max_sync = Dict().tag(sync=True) contour_nlevels_value = Int().tag(sync=True) contour_nlevels_sync = Dict().tag(sync=True) contour_custom_levels_value = List().tag(sync=True) contour_custom_levels_txt = Unicode().tag(sync=True) # controlled by vue contour_custom_levels_sync = Dict().tag(sync=True) axes_visible_value = Bool().tag(sync=True) axes_visible_sync = Dict().tag(sync=True) icon_radialtocheck = Unicode(read_icon(os.path.join(ICON_DIR, 'radialtocheck.svg'), 'svg+xml')).tag(sync=True) # noqa icon_checktoradial = Unicode(read_icon(os.path.join(ICON_DIR, 'checktoradial.svg'), 'svg+xml')).tag(sync=True) # noqa show_viewer_labels = Bool(True).tag(sync=True) def __init__(self, *args, **kwargs): # track whether the stretch histogram needs an update (some entry has changed) if is_active # becomes True, to address potential lag from a backlog of calls to # _update_stretch_histogram if the browser throttles pings # (https://github.com/spacetelescope/jdaviz/issues/2317) self._stretch_histogram_needs_update = True super().__init__(*args, **kwargs) self.viewer = ViewerSelect(self, 'viewer_items', 'viewer_selected', 'multiselect') self.layer = LayerSelect(self, 'layer_items', 'layer_selected', 'viewer_selected', 'multiselect') # noqa def is_profile(state): return isinstance(state, (ProfileViewerState, ProfileLayerState)) def not_profile(state): return not is_profile(state) def is_scatter(state): return isinstance(state, (ScatterViewerState, BqplotScatterLayerState)) def supports_line(state): return is_profile(state) or is_scatter(state) def is_image(state): return isinstance(state, BqplotImageLayerState) def not_image(state): return not is_image(state) def not_image_or_spatial_subset(state): return not is_image(state) and not is_spatial_subset(state) def is_spatial_subset(state): return isinstance(state, ImageSubsetLayerState) def is_not_subset(state): return not is_spatial_subset(state) def line_visible(state): # exclude for scatter layers where the marker is shown instead of the line return getattr(state, 'line_visible', True) def state_attr_for_line_visible(state): if is_scatter(state): return 'line_visible' return 'visible' # Profile/line viewer/layer options: # TODO: once lines are supported in ScatterViewer, update state_filter to supports_line self.line_visible = PlotOptionsSyncState(self, self.viewer, self.layer, state_attr_for_line_visible, # noqa 'line_visible_value', 'line_visible_sync', state_filter=is_profile) self.collapse_function = PlotOptionsSyncState(self, self.viewer, self.layer, 'function', 'collapse_func_value', 'collapse_func_sync') self.line_color = PlotOptionsSyncState(self, self.viewer, self.layer, 'color', 'line_color_value', 'line_color_sync', state_filter=not_image_or_spatial_subset) self.line_width = PlotOptionsSyncState(self, self.viewer, self.layer, 'linewidth', 'line_width_value', 'line_width_sync', state_filter=supports_line) self.line_opacity = PlotOptionsSyncState(self, self.viewer, self.layer, 'alpha', 'line_opacity_value', 'line_opacity_sync', state_filter=supports_line) self.line_as_steps = PlotOptionsSyncState(self, self.viewer, self.layer, 'as_steps', 'line_as_steps_value', 'line_as_steps_sync') self.uncertainty_visible = PlotOptionsSyncState(self, self.viewer, self.layer, 'show_uncertainty', # noqa 'uncertainty_visible_value', 'uncertainty_visible_sync') # noqa # Scatter/marker options: self.marker_visible = PlotOptionsSyncState(self, self.viewer, self.layer, 'visible', 'marker_visible_value', 'marker_visible_sync', state_filter=is_scatter) self.marker_fill = PlotOptionsSyncState(self, self.viewer, self.layer, 'fill', 'marker_fill_value', 'marker_fill_sync', state_filter=is_scatter) self.marker_opacity = PlotOptionsSyncState(self, self.viewer, self.layer, 'alpha', 'marker_opacity_value', 'marker_opacity_sync', state_filter=is_scatter) self.marker_size_mode = PlotOptionsSyncState(self, self.viewer, self.layer, 'size_mode', 'marker_size_mode_value', 'marker_size_mode_sync', # noqa state_filter=is_scatter) self.marker_size = PlotOptionsSyncState(self, self.viewer, self.layer, 'size', 'marker_size_value', 'marker_size_sync', state_filter=is_scatter) self.marker_size_scale = PlotOptionsSyncState(self, self.viewer, self.layer, 'size_scaling', 'marker_size_scale_value', 'marker_size_scale_sync', # noqa state_filter=is_scatter) self.marker_size_col = PlotOptionsSyncState(self, self.viewer, self.layer, 'size_att', 'marker_size_col_value', 'marker_size_col_sync', state_filter=is_scatter) self.marker_size_vmin = PlotOptionsSyncState(self, self.viewer, self.layer, 'size_vmin', 'marker_size_vmin_value', 'marker_size_vmin_sync', # noqa state_filter=is_scatter) self.marker_size_vmax = PlotOptionsSyncState(self, self.viewer, self.layer, 'size_vmax', 'marker_size_vmax_value', 'marker_size_vmax_sync', # noqa state_filter=is_scatter) # TODO: remove marker_ prefix if these also apply to the lines? self.marker_color_mode = PlotOptionsSyncState(self, self.viewer, self.layer, 'cmap_mode', 'marker_color_mode_value', 'marker_color_mode_sync', # noqa state_filter=is_scatter) self.marker_color = PlotOptionsSyncState(self, self.viewer, self.layer, 'color', 'marker_color_value', 'marker_color_sync', state_filter=is_scatter) self.marker_color_col = PlotOptionsSyncState(self, self.viewer, self.layer, 'cmap_att', 'marker_color_col_value', 'marker_color_col_sync', # noqa state_filter=is_scatter) self.marker_colormap = PlotOptionsSyncState(self, self.viewer, self.layer, 'cmap', 'marker_colormap_value', 'marker_colormap_sync', state_filter=is_scatter) self.marker_colormap_vmin = PlotOptionsSyncState(self, self.viewer, self.layer, 'cmap_vmin', 'marker_colormap_vmin_value', 'marker_colormap_vmin_sync', # noqa state_filter=is_scatter) self.marker_colormap_vmax = PlotOptionsSyncState(self, self.viewer, self.layer, 'cmap_vmax', 'marker_colormap_vmax_value', 'marker_colormap_vmax_sync', # noqa state_filter=is_scatter) # Image viewer/layer options: self.stretch_function = PlotOptionsSyncState(self, self.viewer, self.layer, 'stretch', 'stretch_function_value', 'stretch_function_sync', # noqa state_filter=is_image) self.stretch_preset = PlotOptionsSyncState(self, self.viewer, self.layer, 'percentile', 'stretch_preset_value', 'stretch_preset_sync', state_filter=is_image) self.stretch_vmin = PlotOptionsSyncState(self, self.viewer, self.layer, 'v_min', 'stretch_vmin_value', 'stretch_vmin_sync', state_filter=is_image) self.stretch_vmax = PlotOptionsSyncState(self, self.viewer, self.layer, 'v_max', 'stretch_vmax_value', 'stretch_vmax_sync', state_filter=is_image) self.stretch_histogram = Plot(self) self.stretch_histogram.add_bins('histogram', color='gray') self.stretch_histogram.add_line('vmin', x=[0, 0], y=[0, 1], ynorm=True, color='#c75d2c') self.stretch_histogram.add_line('vmax', x=[0, 0], y=[0, 1], ynorm=True, color='#c75d2c') self.stretch_histogram.figure.axes[0].label = 'pixel value' self.stretch_histogram.figure.axes[0].num_ticks = 3 self.stretch_histogram.figure.axes[0].tick_format = '0.1e' self.stretch_histogram.figure.axes[1].label = 'density' self.stretch_histogram.figure.axes[1].num_ticks = 2 self.stretch_histogram_widget = f'IPY_MODEL_{self.stretch_histogram.model_id}' self.subset_visible = PlotOptionsSyncState(self, self.viewer, self.layer, 'visible', 'subset_visible_value', 'subset_visible_sync', state_filter=is_spatial_subset) self.subset_color = PlotOptionsSyncState(self, self.viewer, self.layer, 'color', 'subset_color_value', 'subset_color_sync', state_filter=is_spatial_subset) self.image_visible = PlotOptionsSyncState(self, self.viewer, self.layer, 'bitmap_visible', 'image_visible_value', 'image_visible_sync', state_filter=is_image) self.image_color_mode = PlotOptionsSyncState(self, self.viewer, self.layer, 'color_mode', # noqa 'image_color_mode_value', 'image_color_mode_sync') # noqa self.image_color = PlotOptionsSyncState(self, self.viewer, self.layer, 'color', 'image_color_value', 'image_color_sync', state_filter=is_image) self.image_colormap = PlotOptionsSyncState(self, self.viewer, self.layer, 'cmap', 'image_colormap_value', 'image_colormap_sync') self.image_opacity = PlotOptionsSyncState(self, self.viewer, self.layer, 'alpha', 'image_opacity_value', 'image_opacity_sync', state_filter=is_image) self.image_contrast = PlotOptionsSyncState(self, self.viewer, self.layer, 'contrast', 'image_contrast_value', 'image_contrast_sync') self.image_bias = PlotOptionsSyncState(self, self.viewer, self.layer, 'bias', 'image_bias_value', 'image_bias_sync') self.contour_visible = PlotOptionsSyncState(self, self.viewer, self.layer, 'contour_visible', # noqa 'contour_visible_value', 'contour_visible_sync', spinner='contour_spinner') self.contour_mode = PlotOptionsSyncState(self, self.viewer, self.layer, 'level_mode', 'contour_mode_value', 'contour_mode_sync', spinner='contour_spinner') self.contour_min = PlotOptionsSyncState(self, self.viewer, self.layer, 'c_min', 'contour_min_value', 'contour_min_sync', spinner='contour_spinner') self.contour_max = PlotOptionsSyncState(self, self.viewer, self.layer, 'c_max', 'contour_max_value', 'contour_max_sync', spinner='contour_spinner') self.contour_nlevels = PlotOptionsSyncState(self, self.viewer, self.layer, 'n_levels', 'contour_nlevels_value', 'contour_nlevels_sync', spinner='contour_spinner') self.contour_custom_levels = PlotOptionsSyncState(self, self.viewer, self.layer, 'levels', 'contour_custom_levels_value', 'contour_custom_levels_sync', # noqa spinner='contour_spinner') # Axes options: # axes_visible hidden for imviz in plot_options.vue self.axes_visible = PlotOptionsSyncState(self, self.viewer, self.layer, 'show_axes', 'axes_visible_value', 'axes_visible_sync', state_filter=not_profile) # zoom limits # display_units self.show_viewer_labels = self.app.state.settings['viewer_labels'] self.app.state.add_callback('settings', self._on_app_settings_changed) @property def user_api(self): expose = ['multiselect', 'viewer', 'layer', 'select_all', 'subset_visible'] if self.config == "cubeviz": expose += ['collapse_function'] if self.config != "imviz": expose += ['axes_visible', 'line_visible', 'line_color', 'line_width', 'line_opacity', 'line_as_steps', 'uncertainty_visible'] if self.config != "specviz": expose += ['subset_color', 'stretch_function', 'stretch_preset', 'stretch_vmin', 'stretch_vmax', 'stretch_hist_zoom_limits', 'stretch_hist_nbins', 'image_visible', 'image_color_mode', 'image_color', 'image_colormap', 'image_opacity', 'image_contrast', 'image_bias', 'contour_visible', 'contour_mode', 'contour_min', 'contour_max', 'contour_nlevels', 'contour_custom_levels'] return PluginUserApi(self, expose) @observe('show_viewer_labels') def _on_show_viewer_labels_changed(self, event): self.app.state.settings['viewer_labels'] = event['new'] def _on_app_settings_changed(self, value): self.show_viewer_labels = value['viewer_labels']
[docs] def select_all(self, viewers=True, layers=True): """ Enable multiselect mode and select all viewers and/or layers. Parameters ---------- viewers : bool Whether to select all viewers (default: True) layers: bool Whether to select all layers (default: True) """ self.multiselect = True if viewers: self.viewer.select_all() if layers: self.layer.select_all()
[docs] def vue_unmix_state(self, name): sync_state = getattr(self, name) sync_state.unmix_state()
[docs] def vue_set_value(self, data): attr_name = data.get('name') value = data.get('value') setattr(self, attr_name, value)
@observe('is_active', 'layer_selected', 'viewer_selected', 'stretch_hist_zoom_limits') def _update_stretch_histogram(self, msg={}): if not hasattr(self, 'viewer'): # pragma: no cover # plugin hasn't been fully initialized yet return if not isinstance(msg, dict): # pragma: no cover # then this is from the limits callbacks # IMPORTANT: this assumes the only non-observe callback to this method comes # from state callbacks from zoom limits. if not self.stretch_hist_zoom_limits: # there isn't anything to update, let's not waste resources return # override msg as an empty dict so that the rest of the logic doesn't have to check # its type msg = {} if msg.get('name', None) == 'is_active' and not self._stretch_histogram_needs_update: # this could be re-triggering if the browser is throttling pings on the js-side # and since this is expensive, could result in laggy behavior return elif msg.get('name', None) != 'is_active' and not self.is_active: # next time is_active becomes True, we need to update the histogram plot self._stretch_histogram_needs_update = True return if not self.stretch_function_sync.get('in_subscribed_states'): # pragma: no cover # no (image) viewer with stretch function options return if (not self.viewer.selected or not self.layer.selected): # pragma: no cover # nothing to plot self.stretch_histogram.clear_all_marks() return if self.multiselect and (len(self.viewer.selected) > 1 or len(self.layer.selected) > 1): # pragma: no cover # currently only support single-layer/viewer. For now we'll just clear and return. # TODO: add support for multi-layer/viewer self.stretch_histogram.clear_all_marks() return # Import here to prevent circular import (and not at the top of the method so the import # check is avoided, whenever possible). from jdaviz.configs.imviz.plugins.viewers import ImvizImageView from jdaviz.configs.cubeviz.plugins.viewers import CubevizImageView if not isinstance(self.viewer.selected_obj, (ImvizImageView, CubevizImageView)): # don't update histogram if selected viewer is not an image viewer: return viewer = self.viewer.selected_obj[0] if self.multiselect else self.viewer.selected_obj # manage viewer zoom limit callbacks if ((isinstance(msg, dict) and msg.get('name') == 'viewer_selected') or not self.stretch_hist_zoom_limits): vs = viewer.state for attr in ('x_min', 'x_max', 'y_min', 'y_max'): vs.add_callback(attr, self._update_stretch_histogram) if isinstance(msg, dict) and msg.get('name') == 'viewer_selected': viewer_label_old = msg.get('old')[0] if self.multiselect else msg.get('old') if len(viewer_label_old): vs_old = self.app.get_viewer(viewer_label_old).state for attr in ('x_min', 'x_max', 'y_min', 'y_max'): vs_old.remove_callback(attr, self._update_stretch_histogram) if self.multiselect: data = self.layer.selected_obj[0][0].layer elif len(self.layer.selected_obj): data = self.layer.selected_obj[0].layer else: # skip further updates if no data are available: return comp = data.get_component(data.main_components[0]) # TODO: further optimization could be done by caching sub_data if self.stretch_hist_zoom_limits: if hasattr(viewer, '_get_zoom_limits'): # Viewer limits. This takes account of Imviz linking. xy_limits = viewer._get_zoom_limits(data).astype(int) x_limits = xy_limits[:, 0] y_limits = xy_limits[:, 1] x_min = max(x_limits.min(), 0) x_max = x_limits.max() y_min = max(y_limits.min(), 0) y_max = y_limits.max() sub_data = comp.data[y_min:y_max, x_min:x_max].ravel() else: # spectrum-2d-viewer, for example. We'll assume the viewer # limits correspond to the fixed data components from glue # and filter directly. x_data = data.get_component(data.components[1]).data y_data = data.get_component(data.components[0]).data inds = np.where((x_data >= viewer.state.x_min) & (x_data <= viewer.state.x_max) & (y_data >= viewer.state.y_min) & (y_data <= viewer.state.y_max)) sub_data = comp.data[inds].ravel() else: # include all data, regardless of zoom limits sub_data = comp.data.ravel() # filter out nans (or else bqplot will fail) if np.any(np.isnan(sub_data)): sub_data = sub_data[~np.isnan(sub_data)] hist_mark = self.stretch_histogram.marks['histogram'] with hist_mark.hold_sync(): hist_mark.sample = sub_data interval = PercentileInterval(95) if len(sub_data) > 0: hist_lims = interval.get_limits(sub_data) hist_mark.min, hist_mark.max = hist_lims hist_mark.bins = self.stretch_hist_nbins # in case only the sample has changed but its length has not, # we'll force the traitlet to trigger a change hist_mark.send_state('sample') self._stretch_histogram_needs_update = False @observe('stretch_vmin_value') def _stretch_vmin_changed(self, msg=None): self.stretch_histogram.marks['vmin'].x = [self.stretch_vmin_value, self.stretch_vmin_value] @observe('stretch_vmax_value') def _stretch_vmax_changed(self, msg=None): self.stretch_histogram.marks['vmax'].x = [self.stretch_vmax_value, self.stretch_vmax_value] @observe("stretch_hist_nbins") def _histogram_nbins_changed(self, msg): if self.stretch_histogram is None or msg['new'] == '' or msg['new'] < 1: return self.stretch_histogram.marks['histogram'].bins = self.stretch_hist_nbins
[docs] def set_histogram_x_limits(self, x_min=None, x_max=None): # NOTE: leaving this out of user API until API is finalized with interactive setting self.stretch_histogram.set_xlims(x_min, x_max)
[docs] def set_histogram_y_limits(self, y_min, y_max): # NOTE: leaving this out of user API until API is finalized with interactive setting self.stretch_histogram.set_ylims(y_min, y_max)