Source code for glue_jupyter.app

import ipywidgets as widgets
from IPython.display import display


from glue.core.application_base import Application
from glue.core.link_helpers import LinkSame
from glue.core.roi import PolygonalROI
from glue.core.subset import RoiSubsetState
from glue.core.command import ApplySubsetState
from glue.core.edit_subset_mode import (NewMode, ReplaceMode, AndMode, OrMode,
                                        XorMode, AndNotMode)

from glue_jupyter.utils import _update_not_none
from glue_jupyter.widgets.subset_select import SubsetSelect
from glue_jupyter.widgets.subset_mode import SubsetMode

__all__ = ['JupyterApplication']

# TODO: move this to glue-core so that the subset mode can be set ot a string
# there too
SUBSET_MODES = {'new': NewMode, 'replace': ReplaceMode, 'and': AndMode,
                'or': OrMode, 'xor': XorMode, 'not': AndNotMode}


[docs]class JupyterApplication(Application): """ The main Glue application object for the Jupyter environment. This is used as the primary way to interact with glue, including loading data, creating viewers, and adding links. Parameters ---------- data_collection : `~glue.core.data_collection.DataCollection` A pre-existing data collection. By default, a new data collection is created. session : `~glue.core.session.Session` A pre-existing session object. By default, a new session object is created. """ def __init__(self, data_collection=None, session=None): super(JupyterApplication, self).__init__(data_collection=data_collection, session=session) self.output = widgets.Output() self.widget_data_collection = widgets.SelectMultiple() self.widget_subset_select = SubsetSelect(self.session) self.widget_subset_mode = SubsetMode(self.session) self.widget = widgets.VBox(children=[self.widget_subset_mode, self.output]) def _ipython_display_(self): display(self.widget)
[docs] def set_subset_mode(self, mode): """ Set the current subset mode. By default, selections in viewers update the current subset by replacing the previous selection with the new selection. However it is also possible to combine the current selection with previous selections using boolean operations. Parameters ---------- mode : {'new', 'replace', 'and', 'or', 'xor', 'not'} The selection mode to use. """ if mode in SUBSET_MODES: mode = SUBSET_MODES[mode] self.session.edit_subset_mode.mode = mode
[docs] def new_data_viewer(self, *args, **kwargs): show = kwargs.pop('show', True) viewer = super().new_data_viewer(*args, **kwargs) if show: viewer.show() return viewer
[docs] def histogram1d(self, x=None, data=None, widget='bqplot', color=None, x_min=None, x_max=None, n_bin=None, normalize=False, cumulative=False, viewer_state=None, layer_state=None, show=True): """ Open an interactive histogram viewer. Parameters ---------- x : str or `~glue.core.component_id.ComponentID`, optional The attribute to show on the x axis. data : `~glue.core.data.Data`, optional The initial dataset to show in the viewer. Additional datasets can be added later using the ``add_data`` method on the viewer object. widget : {'bqplot', 'matplotlib'} Whether to use bqplot or Matplotlib as the front-end. color : str or tuple, optional The color to use for the data. Note that this will have the effect of setting the data color for all viewers. x_min : float, optional The lower value of the range to compute the histogram in. x_max : float, optional The upper value of the range to compute the histogram in. n_bin : int, optional The number of bins in the histogram. normalize : bool, optional Whether to normalize the histogram. cumulative : bool, optional Whether to show a cumulative histogram. viewer_state : `~glue.viewers.common.state.ViewerState` The initial state for the viewer (advanced). layer_state : `~glue.viewers.common.state.LayerState` The initial state for the data layer (advanced). show : bool, optional Whether to show the view immediately (`True`) or whether to only show it later if the ``show()`` method is called explicitly (`False`). """ if widget == 'bqplot': from .bqplot.histogram import BqplotHistogramView viewer_cls = BqplotHistogramView elif widget == 'matplotlib': from .matplotlib.histogram import HistogramJupyterViewer viewer_cls = HistogramJupyterViewer else: raise ValueError("Widget type should be 'bqplot' or 'matplotlib'") if data is None: if len(self._data) != 1: raise ValueError('There is more than one data set in the data ' 'collection, please pass a data argument') else: data = self._data[0] viewer_state_obj = viewer_cls._state_cls() viewer_state_obj.x_att_helper.append_data(data) viewer_state = viewer_state or {} if x is not None: viewer_state['x_att'] = data.id[x] # x_min and x_max get set to the hist_x_min/max in # glue.viewers.histogram.state for this API it make more sense to call # it x_min and x_max, and for consistency with the rest _update_not_none(viewer_state, hist_x_min=x_min, hist_x_max=x_max, hist_n_bin=n_bin, normalize=normalize, cumulative=cumulative) viewer_state_obj.update_from_dict(viewer_state) view = self.new_data_viewer(viewer_cls, data=data, state=viewer_state_obj, show=show) layer_state = layer_state or {} _update_not_none(layer_state, color=color) view.layers[0].state.update_from_dict(layer_state) return view
[docs] def scatter2d(self, x=None, y=None, data=None, widget='bqplot', color=None, size=None, viewer_state=None, layer_state=None, show=True): """ Open an interactive 2d scatter plot viewer. Parameters ---------- x : str or `~glue.core.component_id.ComponentID`, optional The attribute to show on the x axis. y : str or `~glue.core.component_id.ComponentID`, optional The attribute to show on the y axis. data : `~glue.core.data.Data`, optional The initial dataset to show in the viewer. Additional datasets can be added later using the ``add_data`` method on the viewer object. widget : {'bqplot', 'matplotlib'} Whether to use bqplot or Matplotlib as the front-end. color : str or tuple, optional The color to use for the markers. Note that this will have the effect of setting the data color for all viewers. size : int or float The size to use for the markers. Note that this will have the effect of setting the marker size for all viewers. viewer_state : `~glue.viewers.common.state.ViewerState` The initial state for the viewer (advanced). layer_state : `~glue.viewers.common.state.LayerState` The initial state for the data layer (advanced). show : bool, optional Whether to show the view immediately (`True`) or whether to only show it later if the ``show()`` method is called explicitly (`False`). """ if widget == 'bqplot': from .bqplot.scatter import BqplotScatterView viewer_cls = BqplotScatterView elif widget == 'matplotlib': from .matplotlib.scatter import ScatterJupyterViewer viewer_cls = ScatterJupyterViewer else: raise ValueError("Widget type should be 'bqplot' or 'matplotlib'") if data is None: if len(self._data) != 1: raise ValueError('There is more than one data set in the data ' 'collection, please pass a data argument') else: data = self._data[0] viewer_state_obj = viewer_cls._state_cls() viewer_state_obj.x_att_helper.append_data(data) viewer_state_obj.y_att_helper.append_data(data) viewer_state = viewer_state or {} if x is not None: viewer_state['x_att'] = data.id[x] if y is not None: viewer_state['y_att'] = data.id[y] viewer_state_obj.update_from_dict(viewer_state) view = self.new_data_viewer(viewer_cls, data=data, state=viewer_state_obj, show=show) layer_state = layer_state or {} _update_not_none(layer_state, color=color, size=size) view.layers[0].state.update_from_dict(layer_state) return view
[docs] def scatter3d(self, x=None, y=None, z=None, data=None, show=True): """ Open an interactive 3d scatter plot viewer. Parameters ---------- x : str or `~glue.core.component_id.ComponentID`, optional The attribute to show on the x axis. y : str or `~glue.core.component_id.ComponentID`, optional The attribute to show on the y axis. z : str or `~glue.core.component_id.ComponentID`, optional The attribute to show on the z axis. data : `~glue.core.data.Data`, optional The initial dataset to show in the viewer. Additional datasets can be added later using the ``add_data`` method on the viewer object. show : bool, optional Whether to show the view immediately (`True`) or whether to only show it later if the ``show()`` method is called explicitly (`False`). """ from .ipyvolume import IpyvolumeScatterView if data is None: if len(self._data) != 1: raise ValueError('There is more than one data set in the data ' 'collection, please pass a data argument') else: data = self._data[0] view = self.new_data_viewer(IpyvolumeScatterView, data=data, show=show) if x is not None: x = data.id[x] view.state.x_att = x if y is not None: y = data.id[y] view.state.y_att = y if z is not None: z = data.id[z] view.state.z_att = z return view
[docs] def imshow(self, x=None, y=None, data=None, widget='bqplot', show=True): """ Open an interactive image viewer. Parameters ---------- x : str or `~glue.core.component_id.ComponentID`, optional The attribute to show on the x axis. This should be one of the pixel axis attributes. y : str or `~glue.core.component_id.ComponentID`, optional The attribute to show on the y axis. This should be one of the pixel axis attributes. data : `~glue.core.data.Data`, optional The initial dataset to show in the viewer. Additional datasets can be added later using the ``add_data`` method on the viewer object. widget : {'bqplot', 'matplotlib'} Whether to use bqplot or Matplotlib as the front-end. show : bool, optional Whether to show the view immediately (`True`) or whether to only show it later if the ``show()`` method is called explicitly (`False`). """ if widget == 'bqplot': from .bqplot.image import BqplotImageView viewer_cls = BqplotImageView elif widget == 'matplotlib': from .matplotlib.image import ImageJupyterViewer viewer_cls = ImageJupyterViewer else: raise ValueError("Widget type should be 'bqplot' or 'matplotlib'") if data is None: if len(self._data) != 1: raise ValueError('There is more than one data set in the data ' 'collection, please pass a data argument') else: data = self._data[0] if len(data.pixel_component_ids) < 2: raise ValueError('Only data with two or more dimensions can be used ' 'as the initial dataset in the image viewer') view = self.new_data_viewer(viewer_cls, data=data, show=show) if x is not None: x = data.id[x] view.state.x_att = x if y is not None: y = data.id[y] view.state.y_att = y return view
[docs] def profile1d(self, x=None, data=None, widget='bqplot', show=True): """ Open an interactive 1d profile viewer. Parameters ---------- x : str or `~glue.core.component_id.ComponentID`, optional The attribute to show on the x axis. This should be a pixel or world coordinate `~glue.core.component_id.ComponentID`. data : `~glue.core.data.Data`, optional The initial dataset to show in the viewer. Additional datasets can be added later using the ``add_data`` method on the viewer object. widget : {'bqplot', 'matplotlib'} Whether to use bqplot or Matplotlib as the front-end. show : bool, optional Whether to show the view immediately (`True`) or whether to only show it later if the ``show()`` method is called explicitly (`False`). """ if widget == 'bqplot': from .bqplot.profile import BqplotProfileView viewer_cls = BqplotProfileView elif widget == 'matplotlib': from .matplotlib.profile import ProfileJupyterViewer viewer_cls = ProfileJupyterViewer else: raise ValueError("Widget type should be 'matplotlib'") if data is None: if len(self._data) != 1: raise ValueError('There is more than one data set in the data ' 'collection, please pass a data argument') else: data = self._data[0] view = self.new_data_viewer(viewer_cls, data=data, show=show) if x is not None: x = data.id[x] view.state.x_att = x return view
[docs] def volshow(self, x=None, y=None, z=None, data=None, show=True): """ Open an interactive volume viewer. Parameters ---------- x : str or `~glue.core.component_id.ComponentID`, optional The attribute to show on the x axis. This should be one of the pixel axis attributes. y : str or `~glue.core.component_id.ComponentID`, optional The attribute to show on the y axis. This should be one of the pixel axis attributes. z : str or `~glue.core.component_id.ComponentID`, optional The attribute to show on the z axis. This should be one of the pixel axis attributes. data : `~glue.core.data.Data`, optional The initial dataset to show in the viewer. Additional datasets can be added later using the ``add_data`` method on the viewer object. show : bool, optional Whether to show the view immediately (`True`) or whether to only show it later if the ``show()`` method is called explicitly (`False`). """ from .ipyvolume import IpyvolumeVolumeView if data is None: if len(self._data) != 1: raise ValueError('There is more than one data set in the data ' 'collection, please pass a data argument') else: data = self._data[0] view = self.new_data_viewer(IpyvolumeVolumeView, data=data, show=show) if x is not None: x = data.id[x] view.state.x_att = x if y is not None: y = data.id[y] view.state.y_att = y if z is not None: z = data.id[z] view.state.z_att = z return view
[docs] def subset(self, name, subset_state): """ Create a new selection/subset. Parameters ---------- name : str The name of the new subset. subset_state : `~glue.core.subset.SubsetState` The definition of the subset. See the documentation at http://docs.glueviz.org/en/stable/python_guide/data_tutorial.html#defining-new-subsets for more information about creating subsets programmatically. """ return self.data_collection.new_subset_group(name, subset_state)
[docs] def subset_lasso2d(self, x_att, y_att, lasso_x, lasso_y): """ Create a subset from a programmatic 2d lasso selection. Parameters ---------- x_att : `~glue.core.component_id.ComponentID` The attribute corresponding to the x values being selected. y_att : `~glue.core.component_id.ComponentID` The attribute corresponding to the x values being selected. lasso_x : iterable The x values of the lasso. lasso_y : iterable The y values of the lasso. """ roi = PolygonalROI(lasso_x, lasso_y) self.subset_roi([x_att, y_att], roi)
[docs] def subset_roi(self, attributes, roi): """ Create a subset from a region of interest. Parameters ---------- attributes : iterable The attributes on the x and y axis roi : `~glue.core.roi.Roi` The region of interest to use to create the subset. """ subset_state = RoiSubsetState(attributes[0], attributes[1], roi) cmd = ApplySubsetState(data_collection=self.data_collection, subset_state=subset_state) self._session.command_stack.do(cmd)
# Methods that we need to override to avoid the default behavior def _update_undo_redo_enabled(self, *args): pass # TODO: if we want a gui for this, we need to update it here @staticmethod def _choose_merge(*args, **kwargs): # Never suggest automatic merging return None, None
[docs] def add_widget(self, widget, label=None, tab=None): pass