From 574dc3848acb0c9a80b5abc4b3d37abffb34ad7d Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Fri, 12 Apr 2019 08:24:24 +0100 Subject: [PATCH 01/10] Started writing documentation --- docs/api.rst | 5 + docs/{source => }/conf.py | 31 +- docs/index.rst | 22 ++ docs/installing.rst | 18 + docs/source/index.ipynb | 141 -------- glue_jupyter/__init__.py | 35 +- glue_jupyter/app.py | 379 ++++++++++++++++----- glue_jupyter/widgets/subset_mode_test.py | 4 +- glue_jupyter/widgets/subset_select_test.py | 2 +- 9 files changed, 392 insertions(+), 245 deletions(-) create mode 100644 docs/api.rst rename docs/{source => }/conf.py (86%) create mode 100644 docs/index.rst create mode 100644 docs/installing.rst delete mode 100644 docs/source/index.ipynb diff --git a/docs/api.rst b/docs/api.rst new file mode 100644 index 00000000..f852f6e2 --- /dev/null +++ b/docs/api.rst @@ -0,0 +1,5 @@ +API Documentation +================= + +.. automodapi:: glue_jupyter + :no-inheritance-diagram: diff --git a/docs/source/conf.py b/docs/conf.py similarity index 86% rename from docs/source/conf.py rename to docs/conf.py index 0258b7d2..689c6166 100644 --- a/docs/source/conf.py +++ b/docs/conf.py @@ -32,11 +32,15 @@ # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. extensions = ['sphinx.ext.autodoc', - 'sphinx.ext.intersphinx', - 'sphinx.ext.todo', - 'sphinx.ext.mathjax', - 'sphinx.ext.viewcode', - 'nbsphinx'] + 'sphinx.ext.intersphinx', + 'sphinx.ext.todo', + 'sphinx.ext.mathjax', + 'sphinx.ext.viewcode', + 'nbsphinx', 'numpydoc', + 'sphinx_automodapi.automodapi', + 'sphinxcontrib.spelling'] + +numpydoc_show_class_members = False # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] @@ -170,8 +174,17 @@ 'Miscellaneous'), ] - - - # Example configuration for intersphinx: refer to the Python standard library. -intersphinx_mapping = {'https://docs.python.org/': None} +intersphinx_cache_limit = 10 # days to keep the cached inventories +intersphinx_mapping = { + # 'sphinx': ('https://www.sphinx-doc.org/en/latest/', None), + 'python': ('https://docs.python.org/3.7', None), + # 'matplotlib': ('https://matplotlib.org', None), + # 'numpy': ('https://docs.scipy.org/doc/numpy', None), + # 'astropy': ('http://docs.astropy.org/en/stable/', None), + # 'echo': ('https://echo.readthedocs.io/en/latest/', None), + 'glue': ('http://docs.glueviz.org/en/stable/', None), +} + +default_role = 'obj' +nitpicky = True diff --git a/docs/index.rst b/docs/index.rst new file mode 100644 index 00000000..73c4ba4e --- /dev/null +++ b/docs/index.rst @@ -0,0 +1,22 @@ +Glue in the Jupyter Notebook +============================ + +About +----- + +The **glue-jupyter** package provides a way to use the `glue +`_ package for multi-dimensional linked-data exploration in +the `Jupyter `_ notebook and Jupyter Lab. This package is +still in early development, but it is already possible to try out some of the +functionality. If you run into any issues or would like to request features, +please head over to our `issue tracker +`_. + +User guide +---------- + +.. toctree:: + :maxdepth: 1 + + installing.rst + api.rst diff --git a/docs/installing.rst b/docs/installing.rst new file mode 100644 index 00000000..642d293d --- /dev/null +++ b/docs/installing.rst @@ -0,0 +1,18 @@ +Installation +============ + +To install the latest developer version of the **glue-jupyter** package as +well as its dependencies, you will need the latest developer version of some +dependencies, including glue itself. If you are using conda, we therefore +recommend that you create a new environment:: + + conda create -n glue-jupyter python=3.7 + +To install glue-jupyter along with all its dependencies, you can do:: + + pip install git+https://github.com/glue-viz/glue-jupyter + +If you are interested in using glue-jupyter in Jupyter Lab, you will need to +also install the following extensions manually:: + + jupyter labextension install @jupyter-widgets/jupyterlab-manager bqplot ipyvolume jupyter-threejs diff --git a/docs/source/index.ipynb b/docs/source/index.ipynb deleted file mode 100644 index 0ad92144..00000000 --- a/docs/source/index.ipynb +++ /dev/null @@ -1,141 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Glue in the Jupyter Notebook\n", - "## Installation\n", - "\n", - "(Note these are not working yet)\n", - "To install use pip:\n", - "\n", - "```\n", - "$ pip install glue-jupyter\n", - "```\n", - "\n", - "To make it work in Jupyter lab:\n", - "```\n", - "$ jupyter labextension install @jupyter-widgets/jupyterlab-manager bqplot ipyvolume jupyter-threejs\n", - "```\n", - "\n", - "You can download the data files for this tutorial here:\n", - " * [w5.tgz](http://docs.glueviz.org/en/stable/_downloads/w5.tgz)\n", - " * [w5.zip](http://docs.glueviz.org/en/stable/_downloads/w5.zip)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "import glue_jupyter as gj" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "image = gj.load('./w5/w5.fits')\n", - "sources = gj.load('./w5/w5_psc.vot')\n", - "app = gj.jglue(image=image, sources=sources)\n", - "app.link([('sources.RAJ2000', 'image.Right Ascension'), ('sources.DEJ2000', 'image.Declination')])" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "sources['Jmag'].shape" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "im = app.imshow()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "layer0 = im.layers[0]\n", - "layer0.state.percentile = 99" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "image.component_ids()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "app.subset_lasso2d(image.id['Pixel Axis 1 [x]'], image.id['Pixel Axis 0 [y]'], [400, 600, 500, 400], [400, 400, 700, 700])" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# app.scatter2d('RAJ2000', 'DEJ2000', data=sources)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "app.histogram1d('RAJ2000', data=sources)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.6.4" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/glue_jupyter/__init__.py b/glue_jupyter/__init__.py index 513d3221..859ee955 100755 --- a/glue_jupyter/__init__.py +++ b/glue_jupyter/__init__.py @@ -1,22 +1,29 @@ from __future__ import absolute_import from ._version import __version__ # noqa +from .app import JupyterApplication # noqa -# from glue.core.session import Session -# from glue.viewers.scatter.layer_artist import ScatterLayerArtist - - -def load(path): - from glue.core.data_factories import load_data - return load_data(path) +__all__ = ['jglue', 'example_data_xyz', 'example_image', 'example_volume', + 'JupyterApplication'] def jglue(*args, **kwargs): + """ + Create a new Jupyter-based glue application. + + It is typically easiest to call this function without arguments and load + data and add links separately in subsequent calls. However, this function + can also take the same inputs as the `~glue.qglue.qglue` function. + + Once this function is called, it will return a + `~glue_jupyter.JupyterApplication` object, which can then be used to + load data, set up links, and create visualizations. See the documentation + for that class for more details. + """ show = kwargs.pop('show', False) from glue.core import DataCollection from glue.qglue import parse_data, parse_links from glue.core.data_factories import load_data - from .app import JupyterApplication links = kwargs.pop('links', None) @@ -38,6 +45,10 @@ def jglue(*args, **kwargs): def example_data_xyz(seed=42, N=500, loc=0, scale=1, label='xyz'): + """ + Create an example dataset with three attributes x, y, and z set to random + values. + """ from glue.core import Data import numpy as np rng = np.random.RandomState(seed) @@ -51,7 +62,9 @@ def example_data_xyz(seed=42, N=500, loc=0, scale=1, label='xyz'): def example_volume(shape=64, limits=[-4, 4]): - """Creates a test data set containing a ball""" + """ + Creates a test 3-d dataset containing a ball. + """ from glue.core import Data import ipyvolume as ipv ball_data = ipv.examples.ball(shape=shape, limits=limits, show=False, draw=False) @@ -61,7 +74,9 @@ def example_volume(shape=64, limits=[-4, 4]): def example_image(shape=64, limits=[-4, 4]): - """Creates a test data set containing a ball""" + """ + Creates a test 2-d dataset containing an image. + """ from glue.core import Data, Coordinates import numpy as np x = np.linspace(-3, 3, num=shape) diff --git a/glue_jupyter/app.py b/glue_jupyter/app.py index 28b0e79c..b27fe4a3 100644 --- a/glue_jupyter/app.py +++ b/glue_jupyter/app.py @@ -7,16 +7,37 @@ 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 AndMode, ReplaceMode +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} + -# not sure we need to inherit: from glue.core.application_base import Application -# what would we gain that would be natural in the notebook? 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) @@ -30,79 +51,86 @@ def _ipython_display_(self): display(self.widget) def link(self, links): + """ + Parse and add links. + """ from glue.qglue import parse_links self.data_collection.add_link(parse_links(self.data_collection, links)) - def add_link(self, data1, attribute1, data2, attribute2, function=None): + def add_link(self, data1, attribute1, data2, attribute2): + """ + Add a simple identity link between two attributes. + + Parameters + ---------- + data1 : `~glue.core.data.Data` + The dataset containing the first attribute. + attribute1 : str or `~glue.core.component_id.ComponentID` + The first attribute to link. + data2 : `~glue.core.data.Data` + The dataset containing the first attribute. + attribute2 : str or `~glue.core.component_id.ComponentID` + The first attribute to link. + """ # For now this assumes attribute1 and attribute2 are strings and single # attributes. In future we should generalize this while keeping the # simplest use case simple. - if function is not None: - raise NotImplementedError att1 = data1.id[attribute1] att2 = data2.id[attribute2] link = LinkSame(att1, att2) self.data_collection.add_link(link) - def subset_mode(self, mode): - self.session.edit_subset_mode.mode = mode - - def subset_mode_replace(self): - self.subset_mode(ReplaceMode) - - def subset_mode_and(self): - self.subset_mode(AndMode) - - def subset_lasso2d(self, x, y, xvalues, yvalues): - roi = PolygonalROI(xvalues, yvalues) - self.subset_roi([x, y], roi) - - def subset_roi(self, components, roi, use_current=False): - subset_state = RoiSubsetState(components[0], components[1], roi) - cmd = ApplySubsetState(data_collection=self.data_collection, - subset_state=subset_state, - use_current=use_current) - self._session.command_stack.do(cmd) - - def _roi_to_subset_state(self, components, roi): - return RoiSubsetState(components[0], components[1], roi) + def set_subset_mode(self, mode): + """ + Set the current subset mode. - def add_widget(self, widget, label=None, tab=None): - pass + 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. - # TODO: remove when https://github.com/glue-viz/glue/pull/1877 is merged - def new_data_viewer(self, viewer_class, data=None, state=None): + Parameters + ---------- + mode : {'new', 'replace', 'and', 'or', 'xor', 'not'} + The selection mode to use. """ - Create a new data viewer, add it to the UI, - and populate with data + if mode in SUBSET_MODES: + mode = SUBSET_MODES[mode] + self.session.edit_subset_mode.mode = mode + 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): + """ + 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). """ - from glue.core import BaseData - - if viewer_class is None: - return - - if state is not None: - c = viewer_class(self._session, state=state) - else: - c = viewer_class(self._session) - c.register_to_hub(self._session.hub) - - if data is not None: - if isinstance(data, BaseData): - result = c.add_data(data) - elif isinstance(data, Subset): - result = c.add_subset(data) - if not result: - c.close(warn=False) - return - - self.add_widget(c) - c.show() - return c - - def histogram1d(self, x=None, data=None, widget='bqplot', color=None, x_min=None, x_max=None, hist_n_bin=None, normalize=False, cumulative=False, viewer_state=None, layer_state=None): if widget == 'bqplot': from .bqplot.histogram import BqplotHistogramView viewer_cls = BqplotHistogramView @@ -111,21 +139,23 @@ def histogram1d(self, x=None, data=None, widget='bqplot', color=None, x_min=None viewer_cls = HistogramJupyterViewer else: raise ValueError("Widget type should be 'bqplot' or 'matplotlib'") - if data is None and len(self._data) != 1: - raise ValueError('There is more than 1 data set in the data collection, please pass a data argument') - data = data or self._data[0] - state = viewer_cls._state_cls() - state.x_att_helper.append_data(data) + 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=hist_n_bin, + _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) @@ -136,6 +166,33 @@ def histogram1d(self, x=None, data=None, widget='bqplot', color=None, x_min=None return view def scatter2d(self, x=None, y=None, data=None, widget='bqplot', color=None, size=None, viewer_state=None, layer_state=None): + """ + 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). + """ + if widget == 'bqplot': from .bqplot.scatter import BqplotScatterView viewer_cls = BqplotScatterView @@ -144,17 +201,23 @@ def scatter2d(self, x=None, y=None, data=None, widget='bqplot', color=None, size viewer_cls = ScatterJupyterViewer else: raise ValueError("Widget type should be 'bqplot' or 'matplotlib'") - if data is None and len(self._data) != 1: - raise ValueError('There is more than 1 data set in the data collection, please pass a data argument') - data = data or self._data[0] + + 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 x 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) @@ -164,10 +227,31 @@ def scatter2d(self, x=None, y=None, data=None, widget='bqplot', color=None, size return view def scatter3d(self, x=None, y=None, z=None, data=None): + """ + 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. + """ + from .ipyvolume import IpyvolumeScatterView - if data is None and len(self._data) != 1: - raise ValueError('There is more than 1 data set in the data collection, please pass a data argument') - data = data or self._data[0] + + 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) if x is not None: x = data.id[x] @@ -181,6 +265,25 @@ def scatter3d(self, x=None, y=None, z=None, data=None): return view def imshow(self, x=None, y=None, data=None, widget='bqplot'): + """ + 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. + """ + if widget == 'bqplot': from .bqplot.image import BqplotImageView viewer_cls = BqplotImageView @@ -189,21 +292,46 @@ def imshow(self, x=None, y=None, data=None, widget='bqplot'): viewer_cls = ImageJupyterViewer else: raise ValueError("Widget type should be 'bqplot' or 'matplotlib'") - data = data or self._data[0] - if data is None and len(self._data) != 1: - raise ValueError('There is more than 1 data set in the data collection, please pass a data argument') + + 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('There are less than 2 pixel components (not an image?)') + 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) + 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 def profile1d(self, x=None, data=None, widget='bqplot'): + """ + 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. + """ + if widget == 'bqplot': from .bqplot.profile import BqplotProfileView viewer_cls = BqplotProfileView @@ -212,32 +340,116 @@ def profile1d(self, x=None, data=None, widget='bqplot'): viewer_cls = ProfileJupyterViewer else: raise ValueError("Widget type should be 'matplotlib'") - data = data or self._data[0] + + 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) + if x is not None: x = data.id[x] view.state.x_att = x + return view - def volshow(self, x="Pixel Axis 2 [x]", y="Pixel Axis 1 [y]", z="Pixel Axis 0 [z]", data=None): + def volshow(self, x=None, y=None, z=None, data=None): + """ + 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. + """ from .ipyvolume import IpyvolumeVolumeView - data = data or self._data[0] - if data is None and len(self._data) != 1: - raise ValueError('There is more than 1 data set in the data collection, please pass a data argument') + + 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) + 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 - def subset(self, name, state): - return self.data_collection.new_subset_group(name, state) + 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) + + 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) + + 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 @@ -246,3 +458,6 @@ def _update_undo_redo_enabled(self, *args): def _choose_merge(*args, **kwargs): # Never suggest automatic merging return None, None + + def add_widget(self, widget, label=None, tab=None): + pass diff --git a/glue_jupyter/widgets/subset_mode_test.py b/glue_jupyter/widgets/subset_mode_test.py index 0b11e47e..6a8655ef 100644 --- a/glue_jupyter/widgets/subset_mode_test.py +++ b/glue_jupyter/widgets/subset_mode_test.py @@ -6,9 +6,9 @@ def test_subset_mode(app, datax, dataxyz, dataxz): # glue -> ui sync assert subset_mode.widget_selection_mode.value == 0 - app.subset_mode_and() + app.set_subset_mode('and') assert subset_mode.widget_selection_mode.value == 2 - app.subset_mode_replace() + app.set_subset_mode('replace') assert subset_mode.widget_selection_mode.value == 0 # ui -> glue sync diff --git a/glue_jupyter/widgets/subset_select_test.py b/glue_jupyter/widgets/subset_select_test.py index dba3d3e5..5a8c0bb3 100644 --- a/glue_jupyter/widgets/subset_select_test.py +++ b/glue_jupyter/widgets/subset_select_test.py @@ -11,7 +11,7 @@ def test_subset_select(app, datax, dataxyz, dataxz): assert not subset_select.widget_menu_item_select_multiple_checkbox.checked assert subset_select.widget_select.value == 'new' - # now we make a section + # now we make a selection app.subset_lasso2d(dataxyz.id['x'], dataxyz.id['y'], [0.5, 2.5, 2.5, 0.5], [1, 1, 3.5, 3.5]) assert len(subset_select.widget_menu_items_subsets) == 1 From 304f702e9e7547fec8be4058156c352366617315 Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Fri, 12 Apr 2019 12:03:55 +0100 Subject: [PATCH 02/10] Added dependencies for docs to setup.cfg --- setup.cfg | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/setup.cfg b/setup.cfg index 88d4f6e2..b76fe769 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,6 +1,6 @@ [metadata] name = glue-jupyter -version = attr: glue_jupyter.__version__ +version = attr: glue_jupyter._version.__version__ url = https://glueviz.org author = Maarten A. Breddels author_email = maartenbreddels@gmail.com @@ -24,3 +24,4 @@ install_requires = [options.extras_require] test = pytest; pytest-cov; runipy; codecov +docs = sphinx; sphinx-automodapi; numpydoc; sphinxcontrib-spelling; nbsphinx; sphinx-rtd-theme From 710a38d65dbeee107bba285fadba9cfb4d2c6ce5 Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Fri, 12 Apr 2019 12:18:02 +0100 Subject: [PATCH 03/10] Added initial getting started page --- .gitignore | 4 +- docs/Makefile | 133 ++++++++++++++++++++++++++++++ docs/getting_started.rst | 40 +++++++++ docs/index.rst | 1 + docs/make.bat | 170 +++++++++++++++++++++++++++++++++++++++ 5 files changed, 346 insertions(+), 2 deletions(-) create mode 100644 docs/Makefile create mode 100644 docs/getting_started.rst create mode 100644 docs/make.bat diff --git a/.gitignore b/.gitignore index 7125406d..ee5bac95 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,7 @@ # Sphinx & coverage build -doc/_build -doc/api +docs/_build +docs/api glue/tests/htmlcov *.coverage *htmlcov* diff --git a/docs/Makefile b/docs/Makefile new file mode 100644 index 00000000..350f5e01 --- /dev/null +++ b/docs/Makefile @@ -0,0 +1,133 @@ +# Makefile for Sphinx documentation +# + +# You can set these variables from the command line. +SPHINXOPTS = -W --keep-going +SPHINXBUILD = sphinx-build +PAPER = +BUILDDIR = _build + +# Internal variables. +PAPEROPT_a4 = -D latex_paper_size=a4 +PAPEROPT_letter = -D latex_paper_size=letter +ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . + +.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest + +#This is needed with git because git doesn't create a dir if it's empty +$(shell [ -d "_static" ] || mkdir -p _static) + +help: + @echo "Please use \`make ' where is one of" + @echo " html to make standalone HTML files" + @echo " dirhtml to make HTML files named index.html in directories" + @echo " singlehtml to make a single large HTML file" + @echo " pickle to make pickle files" + @echo " json to make JSON files" + @echo " htmlhelp to make HTML files and a HTML help project" + @echo " qthelp to make HTML files and a qthelp project" + @echo " devhelp to make HTML files and a Devhelp project" + @echo " epub to make an epub" + @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" + @echo " latexpdf to make LaTeX files and run them through pdflatex" + @echo " text to make text files" + @echo " man to make manual pages" + @echo " changes to make an overview of all changed/added/deprecated items" + @echo " linkcheck to check all external links for integrity" + +clean: + -rm -rf $(BUILDDIR) + -rm -rf api + -rm -rf generated + +html: + $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html + @echo + @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." + +dirhtml: + $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml + @echo + @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." + +singlehtml: + $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml + @echo + @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." + +pickle: + $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle + @echo + @echo "Build finished; now you can process the pickle files." + +json: + $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json + @echo + @echo "Build finished; now you can process the JSON files." + +htmlhelp: + $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp + @echo + @echo "Build finished; now you can run HTML Help Workshop with the" \ + ".hhp project file in $(BUILDDIR)/htmlhelp." + +qthelp: + $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp + @echo + @echo "Build finished; now you can run "qcollectiongenerator" with the" \ + ".qhcp project file in $(BUILDDIR)/qthelp, like this:" + @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/Astropy.qhcp" + @echo "To view the help file:" + @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/Astropy.qhc" + +devhelp: + $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp + @echo + @echo "Build finished." + @echo "To view the help file:" + @echo "# mkdir -p $$HOME/.local/share/devhelp/Astropy" + @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/Astropy" + @echo "# devhelp" + +epub: + $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub + @echo + @echo "Build finished. The epub file is in $(BUILDDIR)/epub." + +latex: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo + @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." + @echo "Run \`make' in that directory to run these through (pdf)latex" \ + "(use \`make latexpdf' here to do that automatically)." + +latexpdf: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo "Running LaTeX files through pdflatex..." + make -C $(BUILDDIR)/latex all-pdf + @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." + +text: + $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text + @echo + @echo "Build finished. The text files are in $(BUILDDIR)/text." + +man: + $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man + @echo + @echo "Build finished. The manual pages are in $(BUILDDIR)/man." + +changes: + $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes + @echo + @echo "The overview file is in $(BUILDDIR)/changes." + +linkcheck: + $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck + @echo + @echo "Link check complete; look for any errors in the above output " \ + "or in $(BUILDDIR)/linkcheck/output.txt." + +doctest: + @echo "Run 'python setup.py test' in the root directory to run doctests " \ + @echo "in the documentation." diff --git a/docs/getting_started.rst b/docs/getting_started.rst new file mode 100644 index 00000000..5b24f53f --- /dev/null +++ b/docs/getting_started.rst @@ -0,0 +1,40 @@ +Getting started +=============== + +To start using glue in the Jupyter notebook or Jupyter lab, you will need to +call the :func:`~glue_jupyter.jglue` function:: + + >>> from glue_jupyter import jglue + >>> app = jglue() + +This will automatically set up a data container (called a *data collection* in +glue) and the ``app`` object can then be used to load data, link data, and +create visualizations. For instance, suppose that you have a CSV file that you +want to visualize. Start off by loading it with:: + + >>> table = app.load_data('mytable.csv') + +The ``table`` variable points to a glue `~glue.core.data.Data` object. For more +information about how to work with and extract values from this kind of object +see `this tutorial `__ +in the main glue documentation. + +You can then create visualizations using methods on ``app`` - for example, to +create a histogram visualization, use `~glue_jupyter.JupyterApplication.histogram1d`:: + + >>> histogram = app.histogram1d(data=table) + +for a 2-d scatter plot, use `~glue_jupyter.JupyterApplication.scatter2d`:: + + >>> scatter2d = app.scatter2d(data=table) + +and for a 3-d scatter plot, use `~glue_jupyter.JupyterApplication.scatter3d`:: + + >>> scatter3d = app.scatter3d(data=table) + +Other available visualizations include +`~glue_jupyter.JupyterApplication.profile1d`, +`~glue_jupyter.JupyterApplication.imshow`, and +`~glue_jupyter.JupyterApplication.volshow`. + +.. TODO: continue writing this page! diff --git a/docs/index.rst b/docs/index.rst index 73c4ba4e..3b5c9af9 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -19,4 +19,5 @@ User guide :maxdepth: 1 installing.rst + getting_started.rst api.rst diff --git a/docs/make.bat b/docs/make.bat new file mode 100644 index 00000000..93dfe92b --- /dev/null +++ b/docs/make.bat @@ -0,0 +1,170 @@ +@ECHO OFF + +REM Command file for Sphinx documentation + +if "%SPHINXBUILD%" == "" ( + set SPHINXBUILD=sphinx-build +) +set BUILDDIR=_build +set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% . +if NOT "%PAPER%" == "" ( + set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS% +) + +if "%1" == "" goto help + +if "%1" == "help" ( + :help + echo.Please use `make ^` where ^ is one of + echo. html to make standalone HTML files + echo. dirhtml to make HTML files named index.html in directories + echo. singlehtml to make a single large HTML file + echo. pickle to make pickle files + echo. json to make JSON files + echo. htmlhelp to make HTML files and a HTML help project + echo. qthelp to make HTML files and a qthelp project + echo. devhelp to make HTML files and a Devhelp project + echo. epub to make an epub + echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter + echo. text to make text files + echo. man to make manual pages + echo. changes to make an overview over all changed/added/deprecated items + echo. linkcheck to check all external links for integrity + echo. doctest to run all doctests embedded in the documentation if enabled + goto end +) + +if "%1" == "clean" ( + for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i + del /q /s %BUILDDIR%\* + goto end +) + +if "%1" == "html" ( + %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The HTML pages are in %BUILDDIR%/html. + goto end +) + +if "%1" == "dirhtml" ( + %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml. + goto end +) + +if "%1" == "singlehtml" ( + %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml. + goto end +) + +if "%1" == "pickle" ( + %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; now you can process the pickle files. + goto end +) + +if "%1" == "json" ( + %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; now you can process the JSON files. + goto end +) + +if "%1" == "htmlhelp" ( + %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; now you can run HTML Help Workshop with the ^ +.hhp project file in %BUILDDIR%/htmlhelp. + goto end +) + +if "%1" == "qthelp" ( + %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; now you can run "qcollectiongenerator" with the ^ +.qhcp project file in %BUILDDIR%/qthelp, like this: + echo.^> qcollectiongenerator %BUILDDIR%\qthelp\Astropy.qhcp + echo.To view the help file: + echo.^> assistant -collectionFile %BUILDDIR%\qthelp\Astropy.ghc + goto end +) + +if "%1" == "devhelp" ( + %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. + goto end +) + +if "%1" == "epub" ( + %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The epub file is in %BUILDDIR%/epub. + goto end +) + +if "%1" == "latex" ( + %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; the LaTeX files are in %BUILDDIR%/latex. + goto end +) + +if "%1" == "text" ( + %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The text files are in %BUILDDIR%/text. + goto end +) + +if "%1" == "man" ( + %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The manual pages are in %BUILDDIR%/man. + goto end +) + +if "%1" == "changes" ( + %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes + if errorlevel 1 exit /b 1 + echo. + echo.The overview file is in %BUILDDIR%/changes. + goto end +) + +if "%1" == "linkcheck" ( + %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck + if errorlevel 1 exit /b 1 + echo. + echo.Link check complete; look for any errors in the above output ^ +or in %BUILDDIR%/linkcheck/output.txt. + goto end +) + +if "%1" == "doctest" ( + %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest + if errorlevel 1 exit /b 1 + echo. + echo.Testing of doctests in the sources finished, look at the ^ +results in %BUILDDIR%/doctest/output.txt. + goto end +) + +:end From e493961e2588e985f2632e3289ece5015e884274 Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Fri, 12 Apr 2019 12:50:42 +0100 Subject: [PATCH 04/10] Add back auto-showing of viewers --- glue_jupyter/app.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/glue_jupyter/app.py b/glue_jupyter/app.py index b27fe4a3..f87e1f63 100644 --- a/glue_jupyter/app.py +++ b/glue_jupyter/app.py @@ -98,6 +98,11 @@ def set_subset_mode(self, mode): mode = SUBSET_MODES[mode] self.session.edit_subset_mode.mode = mode + def new_data_viewer(self, *args, **kwargs): + viewer = super().new_data_viewer(*args, **kwargs) + viewer.show() + return viewer + 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): """ Open an interactive histogram viewer. From d9b3791f783ea45f67eea65d423a32e4afa9d8e6 Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Sat, 13 Apr 2019 23:07:32 +0100 Subject: [PATCH 05/10] Added more API docs and missing __all__ entries --- docs/api.rst | 68 ++++++++++++++++++- docs/conf.py | 20 +++++- glue_jupyter/bqplot/common/viewer.py | 2 + glue_jupyter/bqplot/histogram/layer_artist.py | 2 + glue_jupyter/bqplot/histogram/viewer.py | 2 + glue_jupyter/bqplot/image/layer_artist.py | 2 + glue_jupyter/bqplot/image/viewer.py | 2 + glue_jupyter/bqplot/profile/layer_artist.py | 1 + glue_jupyter/bqplot/profile/viewer.py | 2 + glue_jupyter/bqplot/scatter/layer_artist.py | 4 +- glue_jupyter/bqplot/scatter/viewer.py | 2 + glue_jupyter/ipyvolume/common/viewer.py | 4 +- .../ipyvolume/scatter/layer_artist.py | 2 +- glue_jupyter/ipyvolume/scatter/viewer.py | 2 + glue_jupyter/ipyvolume/volume/layer_artist.py | 2 + glue_jupyter/ipyvolume/volume/viewer.py | 2 + glue_jupyter/view.py | 2 + 17 files changed, 114 insertions(+), 7 deletions(-) diff --git a/docs/api.rst b/docs/api.rst index f852f6e2..dc4c7909 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -1,5 +1,69 @@ API Documentation ================= - +General +------- .. automodapi:: glue_jupyter - :no-inheritance-diagram: + +.. automodapi:: glue_jupyter.app + +.. automodapi:: glue_jupyter.view + +.. automodapi:: glue_jupyter.common.state3d + +.. automodapi:: glue_jupyter.compat + +bqplot viewers +-------------- + +.. automodapi:: glue_jupyter.bqplot.common.viewer + +.. automodapi:: glue_jupyter.bqplot.histogram.layer_artist + +.. automodapi:: glue_jupyter.bqplot.histogram.layer_style_widget + +.. automodapi:: glue_jupyter.bqplot.histogram.viewer_options_widget + +.. automodapi:: glue_jupyter.bqplot.histogram.viewer + +.. automodapi:: glue_jupyter.bqplot.image.layer_artist + +.. automodapi:: glue_jupyter.bqplot.image.layer_style_widget + +.. automodapi:: glue_jupyter.bqplot.image.viewer_options_widget + +.. automodapi:: glue_jupyter.bqplot.image.viewer + +.. automodapi:: glue_jupyter.bqplot.profile.layer_artist + +.. automodapi:: glue_jupyter.bqplot.profile.layer_style_widget + +.. automodapi:: glue_jupyter.bqplot.profile.viewer_options_widget + +.. automodapi:: glue_jupyter.bqplot.profile.viewer + +.. automodapi:: glue_jupyter.bqplot.scatter.layer_artist + +.. automodapi:: glue_jupyter.bqplot.scatter.layer_style_widget + +.. automodapi:: glue_jupyter.bqplot.scatter.viewer_options_widget + +.. automodapi:: glue_jupyter.bqplot.scatter.viewer + +ipyvolume viewers +----------------- + +.. automodapi:: glue_jupyter.ipyvolume.common.viewer + +.. automodapi:: glue_jupyter.ipyvolume.common.viewer_options_widget + +.. automodapi:: glue_jupyter.ipyvolume.scatter.layer_artist + +.. automodapi:: glue_jupyter.ipyvolume.scatter.layer_style_widget + +.. automodapi:: glue_jupyter.ipyvolume.scatter.viewer + +.. automodapi:: glue_jupyter.ipyvolume.volume.layer_artist + +.. automodapi:: glue_jupyter.ipyvolume.volume.layer_style_widget + +.. automodapi:: glue_jupyter.ipyvolume.volume.viewer diff --git a/docs/conf.py b/docs/conf.py index 689c6166..caf9390c 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -182,9 +182,25 @@ # 'matplotlib': ('https://matplotlib.org', None), # 'numpy': ('https://docs.scipy.org/doc/numpy', None), # 'astropy': ('http://docs.astropy.org/en/stable/', None), - # 'echo': ('https://echo.readthedocs.io/en/latest/', None), - 'glue': ('http://docs.glueviz.org/en/stable/', None), + 'echo': ('https://echo.readthedocs.io/en/latest/', None), + 'ipywidgets': ('https://ipywidgets.readthedocs.io/en/stable/', None), + 'traitlets': ('https://traitlets.readthedocs.io/en/stable/', None), + 'glue': ('http://docs.glueviz.org/en/latest/', None), } default_role = 'obj' nitpicky = True + +nitpick_ignore = [('py:class', 'ipywidgets.widgets.widget_box.Box'), + ('py:class', 'ipywidgets.widgets.widget_box.VBox'), + ('py:class', 'ipywidgets.widgets.widget.Widget'), + ('py:class', 'ipywidgets.widgets.widget.LoggingHasTraits'), + ('py:class', 'ipywidgets.widgets.domwidget.DOMWidget'), + ('py:class', 'ipywidgets.widgets.widget_core.CoreWidget'), + ('py:class', 'traitlets.traitlets.HasTraits'), + ('py:class', 'traitlets.traitlets.HasDescriptors'), + ('py:class', 'glue.external.echo.core.HasCallbackProperties'), + ('py:class', 'glue.viewers.image.layer_artist.ImageLayerArtist'), + ('py:class', 'glue.viewers.image.layer_artist.BaseImageLayerArtist'), + ('py:class', 'glue_vispy_viewers.volume.layer_state.VolumeLayerState'), + ('py:class', 'glue_vispy_viewers.common.layer_state.VispyLayerState')] diff --git a/glue_jupyter/bqplot/common/viewer.py b/glue_jupyter/bqplot/common/viewer.py index 9233118c..8a5e9a33 100644 --- a/glue_jupyter/bqplot/common/viewer.py +++ b/glue_jupyter/bqplot/common/viewer.py @@ -12,6 +12,8 @@ from ...link import link, on_change from ...utils import float_or_none +__all__ = ['BqplotBaseView'] + ICON_WIDTH = 20 icon_brush = widgets.Image.from_file(glue.icons.icon_path('glue_square', icon_format='svg'), width=ICON_WIDTH) diff --git a/glue_jupyter/bqplot/histogram/layer_artist.py b/glue_jupyter/bqplot/histogram/layer_artist.py index 51bcc71a..14adfcbf 100644 --- a/glue_jupyter/bqplot/histogram/layer_artist.py +++ b/glue_jupyter/bqplot/histogram/layer_artist.py @@ -11,6 +11,8 @@ # FIXME: monkey patch ipywidget to accept anything tt.Color.validate = lambda self, obj, value: value +__all__ = ['BqplotHistogramLayerArtist'] + class BqplotHistogramLayerArtist(LayerArtist): diff --git a/glue_jupyter/bqplot/histogram/viewer.py b/glue_jupyter/bqplot/histogram/viewer.py index 5c7f7126..3f8cbe42 100644 --- a/glue_jupyter/bqplot/histogram/viewer.py +++ b/glue_jupyter/bqplot/histogram/viewer.py @@ -8,6 +8,8 @@ from .layer_style_widget import HistogramLayerStateWidget from .viewer_options_widget import HistogramViewerStateWidget +__all__ = ['BqplotHistogramView'] + class BqplotHistogramView(BqplotBaseView): diff --git a/glue_jupyter/bqplot/image/layer_artist.py b/glue_jupyter/bqplot/image/layer_artist.py index fdf79f9a..528fe68b 100644 --- a/glue_jupyter/bqplot/image/layer_artist.py +++ b/glue_jupyter/bqplot/image/layer_artist.py @@ -4,6 +4,8 @@ from .frb_mark import FRBImage +__all__ = ['BqplotImageLayerArtist', 'BqplotImageSubsetLayerArtist'] + class BqplotImageLayerArtist(ImageLayerArtist): diff --git a/glue_jupyter/bqplot/image/viewer.py b/glue_jupyter/bqplot/image/viewer.py index e0b43fa2..5a3b4c32 100644 --- a/glue_jupyter/bqplot/image/viewer.py +++ b/glue_jupyter/bqplot/image/viewer.py @@ -15,6 +15,8 @@ from .layer_style_widget import ImageLayerStateWidget, ImageSubsetLayerStateWidget from .viewer_options_widget import ImageViewerStateWidget +__all__ = ['BqplotImageView'] + class BqplotImageView(BqplotBaseView): diff --git a/glue_jupyter/bqplot/profile/layer_artist.py b/glue_jupyter/bqplot/profile/layer_artist.py index d48cc9f9..e1c142a6 100644 --- a/glue_jupyter/bqplot/profile/layer_artist.py +++ b/glue_jupyter/bqplot/profile/layer_artist.py @@ -17,6 +17,7 @@ from ...link import link +__all__ = ['BqplotProfileLayerArtist'] # FIXME: monkey patch ipywidget to accept anything tt.Color.validate = lambda self, obj, value: value diff --git a/glue_jupyter/bqplot/profile/viewer.py b/glue_jupyter/bqplot/profile/viewer.py index ff5808a4..dd47eac0 100644 --- a/glue_jupyter/bqplot/profile/viewer.py +++ b/glue_jupyter/bqplot/profile/viewer.py @@ -8,6 +8,8 @@ from .layer_style_widget import ProfileLayerStateWidget from .viewer_options_widget import ProfileViewerStateWidget +__all__ = ['BqplotProfileView'] + class BqplotProfileView(BqplotBaseView): diff --git a/glue_jupyter/bqplot/scatter/layer_artist.py b/glue_jupyter/bqplot/scatter/layer_artist.py index 0d0f3d58..23fae0ec 100755 --- a/glue_jupyter/bqplot/scatter/layer_artist.py +++ b/glue_jupyter/bqplot/scatter/layer_artist.py @@ -1,7 +1,6 @@ import numpy as np import bqplot from ipyastroimage.astroimage import AstroImage -import ipywidgets as widgets import ipywidgets.widgets.trait_types as tt from glue.core.data import Subset @@ -17,6 +16,9 @@ tt.Color.validate = lambda self, obj, value: value +__all__ = ['BqplotScatterLayerState', 'BqplotScatterLayerArtist'] + + class BqplotScatterLayerState(ScatterLayerState): bins = CallbackProperty(128, docstring='The number of bins in each dimension for the density map') diff --git a/glue_jupyter/bqplot/scatter/viewer.py b/glue_jupyter/bqplot/scatter/viewer.py index 44963af4..97eacaba 100644 --- a/glue_jupyter/bqplot/scatter/viewer.py +++ b/glue_jupyter/bqplot/scatter/viewer.py @@ -6,6 +6,8 @@ from .viewer_options_widget import ScatterViewerStateWidget from .layer_style_widget import ScatterLayerStateWidget +__all__ = ['BqplotScatterView'] + class BqplotScatterView(BqplotBaseView): diff --git a/glue_jupyter/ipyvolume/common/viewer.py b/glue_jupyter/ipyvolume/common/viewer.py index d1d2f7d8..a35779af 100644 --- a/glue_jupyter/ipyvolume/common/viewer.py +++ b/glue_jupyter/ipyvolume/common/viewer.py @@ -12,10 +12,12 @@ from glue.core.command import ApplySubsetState from ...view import IPyWidgetView -from ...link import link, dlink +from ...link import dlink from .viewer_options_widget import Viewer3DStateWidget +__all__ = ['IpyvolumeBaseView'] + class IpyvolumeBaseView(IPyWidgetView): allow_duplicate_data = False diff --git a/glue_jupyter/ipyvolume/scatter/layer_artist.py b/glue_jupyter/ipyvolume/scatter/layer_artist.py index 9ba14f99..8fd0153b 100755 --- a/glue_jupyter/ipyvolume/scatter/layer_artist.py +++ b/glue_jupyter/ipyvolume/scatter/layer_artist.py @@ -10,7 +10,7 @@ from ...link import link, on_change -__all__ = ['IpyvolumeScatterLayerArtist'] +__all__ = ['Scatter3dLayerState', 'IpyvolumeScatterLayerArtist'] class Scatter3dLayerState(ScatterLayerState): diff --git a/glue_jupyter/ipyvolume/scatter/viewer.py b/glue_jupyter/ipyvolume/scatter/viewer.py index ef32aa06..8e9a3585 100644 --- a/glue_jupyter/ipyvolume/scatter/viewer.py +++ b/glue_jupyter/ipyvolume/scatter/viewer.py @@ -4,6 +4,8 @@ from ..common.viewer_options_widget import Viewer3DStateWidget from ..common.viewer import IpyvolumeBaseView +__all__ = ['IpyvolumeScatterView'] + class IpyvolumeScatterView(IpyvolumeBaseView): diff --git a/glue_jupyter/ipyvolume/volume/layer_artist.py b/glue_jupyter/ipyvolume/volume/layer_artist.py index 0d0bfc4e..0e66719f 100644 --- a/glue_jupyter/ipyvolume/volume/layer_artist.py +++ b/glue_jupyter/ipyvolume/volume/layer_artist.py @@ -10,6 +10,8 @@ from ...link import link, on_change +__all__ = ['IpyvolumeLayerState'] + class IpyvolumeLayerState(VolumeLayerState): diff --git a/glue_jupyter/ipyvolume/volume/viewer.py b/glue_jupyter/ipyvolume/volume/viewer.py index b1962200..25c07798 100644 --- a/glue_jupyter/ipyvolume/volume/viewer.py +++ b/glue_jupyter/ipyvolume/volume/viewer.py @@ -9,6 +9,8 @@ from ..common.viewer_options_widget import Viewer3DStateWidget from ..common.viewer import IpyvolumeBaseView +__all__ = ['IpyvolumeVolumeView'] + class IpyvolumeVolumeView(IpyvolumeBaseView): diff --git a/glue_jupyter/view.py b/glue_jupyter/view.py index 58768197..afca49bf 100644 --- a/glue_jupyter/view.py +++ b/glue_jupyter/view.py @@ -5,6 +5,8 @@ from glue_jupyter.utils import _update_not_none +__all__ = ['IPyWidgetView', 'IPyWidgetLayerArtistContainer'] + class IPyWidgetLayerArtistContainer(LayerArtistContainer): From dcde2b3f384369181840ace44e9c88c4631aabdd Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Sat, 13 Apr 2019 23:08:37 +0100 Subject: [PATCH 06/10] Use automodapi_inheritance_diagram option in sphinx-automodapi --- docs/conf.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/conf.py b/docs/conf.py index caf9390c..029b2f49 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -190,7 +190,6 @@ default_role = 'obj' nitpicky = True - nitpick_ignore = [('py:class', 'ipywidgets.widgets.widget_box.Box'), ('py:class', 'ipywidgets.widgets.widget_box.VBox'), ('py:class', 'ipywidgets.widgets.widget.Widget'), @@ -204,3 +203,5 @@ ('py:class', 'glue.viewers.image.layer_artist.BaseImageLayerArtist'), ('py:class', 'glue_vispy_viewers.volume.layer_state.VolumeLayerState'), ('py:class', 'glue_vispy_viewers.common.layer_state.VispyLayerState')] + +automodapi_inheritance_diagram = False From bfc0e3e7c2b985272812aeec2dd8fa89da68d3b4 Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Sun, 14 Apr 2019 12:36:03 +0100 Subject: [PATCH 07/10] Improve API docs and import all key classes into top-level package for each viewer --- docs/api.rst | 50 ++++------------------ docs/conf.py | 1 + glue_jupyter/bqplot/common/__init__.py | 2 +- glue_jupyter/bqplot/histogram/__init__.py | 5 ++- glue_jupyter/bqplot/image/__init__.py | 5 ++- glue_jupyter/bqplot/profile/__init__.py | 5 ++- glue_jupyter/bqplot/scatter/__init__.py | 5 ++- glue_jupyter/ipyvolume/common/__init__.py | 2 + glue_jupyter/ipyvolume/scatter/__init__.py | 4 +- glue_jupyter/ipyvolume/volume/__init__.py | 4 +- 10 files changed, 34 insertions(+), 49 deletions(-) diff --git a/docs/api.rst b/docs/api.rst index dc4c7909..f39a0df8 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -15,55 +15,21 @@ General bqplot viewers -------------- -.. automodapi:: glue_jupyter.bqplot.common.viewer +.. automodapi:: glue_jupyter.bqplot.common -.. automodapi:: glue_jupyter.bqplot.histogram.layer_artist +.. automodapi:: glue_jupyter.bqplot.histogram -.. automodapi:: glue_jupyter.bqplot.histogram.layer_style_widget +.. automodapi:: glue_jupyter.bqplot.image -.. automodapi:: glue_jupyter.bqplot.histogram.viewer_options_widget +.. automodapi:: glue_jupyter.bqplot.profile -.. automodapi:: glue_jupyter.bqplot.histogram.viewer - -.. automodapi:: glue_jupyter.bqplot.image.layer_artist - -.. automodapi:: glue_jupyter.bqplot.image.layer_style_widget - -.. automodapi:: glue_jupyter.bqplot.image.viewer_options_widget - -.. automodapi:: glue_jupyter.bqplot.image.viewer - -.. automodapi:: glue_jupyter.bqplot.profile.layer_artist - -.. automodapi:: glue_jupyter.bqplot.profile.layer_style_widget - -.. automodapi:: glue_jupyter.bqplot.profile.viewer_options_widget - -.. automodapi:: glue_jupyter.bqplot.profile.viewer - -.. automodapi:: glue_jupyter.bqplot.scatter.layer_artist - -.. automodapi:: glue_jupyter.bqplot.scatter.layer_style_widget - -.. automodapi:: glue_jupyter.bqplot.scatter.viewer_options_widget - -.. automodapi:: glue_jupyter.bqplot.scatter.viewer +.. automodapi:: glue_jupyter.bqplot.scatter ipyvolume viewers ----------------- -.. automodapi:: glue_jupyter.ipyvolume.common.viewer - -.. automodapi:: glue_jupyter.ipyvolume.common.viewer_options_widget - -.. automodapi:: glue_jupyter.ipyvolume.scatter.layer_artist - -.. automodapi:: glue_jupyter.ipyvolume.scatter.layer_style_widget - -.. automodapi:: glue_jupyter.ipyvolume.scatter.viewer - -.. automodapi:: glue_jupyter.ipyvolume.volume.layer_artist +.. automodapi:: glue_jupyter.ipyvolume.common -.. automodapi:: glue_jupyter.ipyvolume.volume.layer_style_widget +.. automodapi:: glue_jupyter.ipyvolume.scatter -.. automodapi:: glue_jupyter.ipyvolume.volume.viewer +.. automodapi:: glue_jupyter.ipyvolume.volume diff --git a/docs/conf.py b/docs/conf.py index 029b2f49..594ebfa6 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -38,6 +38,7 @@ 'sphinx.ext.viewcode', 'nbsphinx', 'numpydoc', 'sphinx_automodapi.automodapi', + 'sphinx_automodapi.smart_resolver', 'sphinxcontrib.spelling'] numpydoc_show_class_members = False diff --git a/glue_jupyter/bqplot/common/__init__.py b/glue_jupyter/bqplot/common/__init__.py index 525d8135..dda0e670 100644 --- a/glue_jupyter/bqplot/common/__init__.py +++ b/glue_jupyter/bqplot/common/__init__.py @@ -1 +1 @@ -from .viewer import BqplotBaseView # noqa +from .viewer import * # noqa diff --git a/glue_jupyter/bqplot/histogram/__init__.py b/glue_jupyter/bqplot/histogram/__init__.py index 078f74de..16ea53ad 100644 --- a/glue_jupyter/bqplot/histogram/__init__.py +++ b/glue_jupyter/bqplot/histogram/__init__.py @@ -1 +1,4 @@ -from .viewer import BqplotHistogramView # noqa +from .layer_artist import * # noqa +from .layer_style_widget import * # noqa +from .viewer_options_widget import * # noqa +from .viewer import * # noqa diff --git a/glue_jupyter/bqplot/image/__init__.py b/glue_jupyter/bqplot/image/__init__.py index d173adef..16ea53ad 100644 --- a/glue_jupyter/bqplot/image/__init__.py +++ b/glue_jupyter/bqplot/image/__init__.py @@ -1 +1,4 @@ -from .viewer import BqplotImageView # noqa +from .layer_artist import * # noqa +from .layer_style_widget import * # noqa +from .viewer_options_widget import * # noqa +from .viewer import * # noqa diff --git a/glue_jupyter/bqplot/profile/__init__.py b/glue_jupyter/bqplot/profile/__init__.py index f9313309..16ea53ad 100644 --- a/glue_jupyter/bqplot/profile/__init__.py +++ b/glue_jupyter/bqplot/profile/__init__.py @@ -1 +1,4 @@ -from .viewer import BqplotProfileView # noqa +from .layer_artist import * # noqa +from .layer_style_widget import * # noqa +from .viewer_options_widget import * # noqa +from .viewer import * # noqa diff --git a/glue_jupyter/bqplot/scatter/__init__.py b/glue_jupyter/bqplot/scatter/__init__.py index 7c84f0f6..16ea53ad 100644 --- a/glue_jupyter/bqplot/scatter/__init__.py +++ b/glue_jupyter/bqplot/scatter/__init__.py @@ -1 +1,4 @@ -from .viewer import BqplotScatterView # noqa +from .layer_artist import * # noqa +from .layer_style_widget import * # noqa +from .viewer_options_widget import * # noqa +from .viewer import * # noqa diff --git a/glue_jupyter/ipyvolume/common/__init__.py b/glue_jupyter/ipyvolume/common/__init__.py index e69de29b..f11590fd 100644 --- a/glue_jupyter/ipyvolume/common/__init__.py +++ b/glue_jupyter/ipyvolume/common/__init__.py @@ -0,0 +1,2 @@ +from .viewer_options_widget import * # noqa +from .viewer import * # noqa diff --git a/glue_jupyter/ipyvolume/scatter/__init__.py b/glue_jupyter/ipyvolume/scatter/__init__.py index 2f4b7dea..39dd5964 100644 --- a/glue_jupyter/ipyvolume/scatter/__init__.py +++ b/glue_jupyter/ipyvolume/scatter/__init__.py @@ -1 +1,3 @@ -from .layer_artist import IpyvolumeScatterLayerArtist # noqa +from .layer_artist import * # noqa +from .layer_style_widget import * # noqa +from .viewer import * # noqa diff --git a/glue_jupyter/ipyvolume/volume/__init__.py b/glue_jupyter/ipyvolume/volume/__init__.py index 729254bf..39dd5964 100644 --- a/glue_jupyter/ipyvolume/volume/__init__.py +++ b/glue_jupyter/ipyvolume/volume/__init__.py @@ -1 +1,3 @@ -from .layer_artist import IpyvolumeVolumeLayerArtist # noqa +from .layer_artist import * # noqa +from .layer_style_widget import * # noqa +from .viewer import * # noqa From de6407c8708fbf52040b05622c67c54618a10e64 Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Sun, 14 Apr 2019 12:36:54 +0100 Subject: [PATCH 08/10] Added docs test to Travis configuration --- .travis.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.travis.yml b/.travis.yml index 7f07f882..2135a920 100644 --- a/.travis.yml +++ b/.travis.yml @@ -29,6 +29,8 @@ script: - pytest --cov glue_jupyter glue_jupyter -p no:warnings - pip install astroquery pyyaml - python .validate-notebooks.py + - cd docs + - make html after_success: - codecov From 877709317e4950336b968950ecfc9a8e4d439d9d Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Sun, 14 Apr 2019 18:01:21 +0100 Subject: [PATCH 09/10] Make sure docs dependencies are installed on Travis --- .travis.yml | 7 +++++++ setup.cfg | 14 ++++++++++++-- 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 2135a920..e5ff4264 100644 --- a/.travis.yml +++ b/.travis.yml @@ -26,9 +26,16 @@ install: - pip install -e .[test] script: + + # Run test suite - pytest --cov glue_jupyter glue_jupyter -p no:warnings + + # Check notebooks - pip install astroquery pyyaml - python .validate-notebooks.py + + # Test documentation build + - pip install -e .[docs] - cd docs - make html diff --git a/setup.cfg b/setup.cfg index b76fe769..287c42c0 100644 --- a/setup.cfg +++ b/setup.cfg @@ -23,5 +23,15 @@ install_requires = ipymaterialui @ git+https://github.com/maartenbreddels/ipymaterialui#egg=ipymaterialui [options.extras_require] -test = pytest; pytest-cov; runipy; codecov -docs = sphinx; sphinx-automodapi; numpydoc; sphinxcontrib-spelling; nbsphinx; sphinx-rtd-theme +test = + pytest + pytest-cov + runipy + codecov +docs = + sphinx + sphinx-automodapi @ git+https://github.com/astropy/sphinx-automodapi#egg=sphinx-automodapi + numpydoc + sphinxcontrib-spelling + nbsphinx + sphinx-rtd-theme From 7bd220f7e36a37ea1c5ecb04cf7a1e8c716dd60c Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Mon, 15 Apr 2019 08:48:25 +0100 Subject: [PATCH 10/10] Don't include spelling extension in dependencies --- docs/conf.py | 3 +-- setup.cfg | 1 - 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index 594ebfa6..495d8b7d 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -38,8 +38,7 @@ 'sphinx.ext.viewcode', 'nbsphinx', 'numpydoc', 'sphinx_automodapi.automodapi', - 'sphinx_automodapi.smart_resolver', - 'sphinxcontrib.spelling'] + 'sphinx_automodapi.smart_resolver'] numpydoc_show_class_members = False diff --git a/setup.cfg b/setup.cfg index 287c42c0..2df4afd7 100644 --- a/setup.cfg +++ b/setup.cfg @@ -32,6 +32,5 @@ docs = sphinx sphinx-automodapi @ git+https://github.com/astropy/sphinx-automodapi#egg=sphinx-automodapi numpydoc - sphinxcontrib-spelling nbsphinx sphinx-rtd-theme