Glue Documentation

_images/logo.png

Glue is a Python library to explore relationships within and among related datasets. Its main features include:

  • Linked Statistical Graphics. With Glue, users can create scatter plots, histograms and images (2D and 3D) of their data. Glue is focused on the brushing and linking paradigm, where selections in any graph propagate to all others.
  • Flexible linking across data. Glue uses the logical links that exist between different data sets to overlay visualizations of different data, and to propagate selections across data sets. These links are specified by the user, and are arbitrarily flexible.
  • Full scripting capability. Glue is written in Python, and built on top of its standard scientific libraries (i.e., Numpy, Matplotlib, Scipy). Users can easily integrate their own python code for data input, cleaning, and analysis.

For more demos, check out the videos page.

The latest version of glue is v0.7 - see our overview of changes in v0.7

Getting started

Glue is designed with “data-hacking” workflows in mind, and can be used in different ways. For instance, you can simply make use of the graphical Glue application as is, and never type a line of code. However, you can also interact with Glue via Python in different ways:

  • Using the IPython terminal built-in to the Glue application
  • Sending data in the form of NumPy arrays or Pandas DataFrames to Glue for exploration from a Python or IPython session.
  • Customizing/hacking your Glue setup using config.py files, including automatically loading and clean data before starting Glue, writing custom functions to parse files in your favorite file format, writing custom functions to link datasets, or creating your own data viewers.

Glue thus blurs the boundary between GUI-centric and code-centric data exploration. In addition, it is also possible to develop your own plugin packages for Glue that you can distribute to users separately, and you can also make use of the Glue framework in your own application to provide data linking capabilities.

In the following sections, we cover the different ways of using Glue from the Glue application to the more advanced ways of interacting with Glue from Python.

Note

For any questions or help with using glue, you can always join the user support mailing list or ask questions on our Gitter channel!

Using the Glue application

Installing Glue

There are several ways to install Glue on your computer:

Enthought Canopy

Platforms: MacOS X, Linux, and Windows

The Enthought Python Distribution includes most but not all non-trivial dependencies.

You can install Glue using:

pip install glueviz

You can then install any additional (optional) Glue dependencies by running:

glue-deps install

on the command line. For more information on glue-deps, see below

Standalone Application

Platforms: MacOS X

Mac users with OS X >= 10.7 can download Glue as a standalone program. This is the fastest way to get started with using Glue, but this application includes its own version of Python, and will not recognize any packages in other Python installations. If you want to use glue in your existing Python installation, follow instructions in the other sections.

Building from Source (For the Brave)

Platforms: MacOS X, Linux, and Windows

The source code for Glue is available on GitHub. Glue relies upon a number of scientific python libraries, as well as the Qt GUI library. Installing these packages is somewhat beyond the scope of this document, and unforunately trickier than it should be. If you want to dive in, here is the basic strategy:

  • Install Qt 4 and either PyQt4 or PySide. If at all possible, use the binary installers; building PyQt4 or PySide from source is tricky (this is a euphemism).
  • Install Glue using pip: pip install glueviz. Alternatively, git clone the repository and install via python setup.py install
  • Install Glue’s remaining dependencies by running glue-deps install. For more information on these dependencies see below.
Dependencies

Glue has the following required dependencies:

And the following optional dependencies are also highly recommended:

In addition to these, there are several other optional dependencies to suport various I/O and other optional functionality. Glue includes a command line utility glue-deps to manage dependencies:

  • Calling glue-deps list displays all of Glue’s required and optional dependencies, along with whether or not each library is already installed on your system. For missing dependencies, the program also provides a brief description of how it is used within Glue.
  • Calling glue-deps install attempts to pip install all missing libraries. You can install single libraries or categories of libraries by providing additional arguments to glue-deps install.
Tips for Ubuntu

Many dependencies can be reliably installed with apt:

sudo apt-get install python-numpy
sudo apt-get install python-scipy
sudo apt-get install python-matplotlib
sudo apt-get install python-qt4
sudo apt-get install pyqt4-dev-tools
sudo apt-get install ipython
sudo apt-get install python-zmq
sudo apt-get install python-pygments
MacPorts

Many dependencies can be reliably installed with:

sudo port install python27
sudo port install py27-numpy
sudo port install py27-scipy
sudo port install py27-matplotlib
sudo port install py27-pyqt4
sudo port install py27-ipython
sudo port install py27-pip

For information about using MacPorts to manage your Python installation, see here

Running Glue

Installing glue from source will create a executable glue script that should be in your path. Running glue from the command line will start the program. Glue accepts a variety of command-line arguments. See glue --help for examples.

Note

On Windows, installation creates an executable glue.exe file within the python script directory (e.g., C:\Python27\Scripts). Windows users can create a desktop shortcut for this file, and run Glue by double clicking on the icon.

Known issues

Issue with PyQt4 from conda

On certain Linux installations, when using Anaconda/conda to manage the Python installation you are using for glue, you may run into the following error when launching glue:

ImportError: /usr/lib/libkdecore.so.5: undefined symbol: _ZNK7QSslKey9algorithmEv

This is due to a known issue with Anaconda where the system installation of Qt is used instead of the version shipped with Anaconda (see this issue if you are interested in a discussion of the issue). A simple workaround is to force glue to use PySide insead of PyQt4:

conda install pyside
export QT_API=pyside

after which glue will use PySide when started.

Getting started

This page walks through Glue’s basic GUI features, using data from the W5 star forming region as an example. You can download the data files for this tutorial here

After installing Glue, open the program by either double clicking on the icon (if using a pre-built application) or from the command line:

glue
_images/main_window.png

Glue’s main interface

The main window consists of 3 areas:

  1. The data manager. This lists all open data sets and subsets (highlighted regions).
  2. The visualization area. This is where each visualization window resides.
  3. The visualization dashboard. This shows the options for the active visualization window.

Opening Data

There are three ways to open data: By clicking on the red folder icon, the File->Open Data Set menu, or Ctrl+O (Cmd+O on the Mac). Find and open the file w5.fits. This is a WISE image of the W5 Star Forming Region.

Plotting Data

After opening w5.fits, a new entry will appear in the data manger:

_images/data_open.png

To visualize a dataset, click and drag the entry from the data manager to the visualization dashboard. A popup window asks about what kind of plot to make. Since this is an image, select Image.

Defining Subsets

Work in glue revolves around “drilling down” into interesting subsets within data. Each visualization type (image, scatterplot, …) provides different ways for defining these subsets. In particular, the image window provides 4 options:

_images/image_selectors.png
  • Rectangular selection: When active, a click+drag defines rectangular regions of interest.
  • Circular selection: Defines circles.
  • Freeform selection: Defines arbitrary polygons.
  • Contour selection: Uses the contour line that passes through the mouse.

We can highlight the west arm of W5 using the rectangle selector:

_images/w5_west.png

Notice that this highlights the relevant pixels in the image, adds a new subset (which we’ve named west arm) to the data manager, and adds a new visualization layer (also labeled west arm (w5)) in the visualization dashboard.

We can redefine this subset by dragging a new rectangle in the image, or we can also move around the current subset by pressing the ‘control’ key and clicking on the subset then dragging it. Alternately, we could define a second subset by clicking the New Subset button (next to the folder button).

Note

When multiple subsets are defined, only the highlighted entries in the data manager are affected when drawing new subsets. If no subsets are highlighted, then a new subset is created.

You can edit the properties of a visualization layer (color, name, etc.) By double-clicking on the entry in the visualization dashboard.

_images/layer_options.png

Likewise, you can re-arrange the rows in this widget to change the order in which each layer is drawn – the top entry will appear above all other entries.

Refining Subsets and Linked Views

Visualizations are linked in Glue – that is, we can plot this data in many different ways, to better understand the properties of each subset. To see this, click and drag the W5 entry into the visualization area a second time, and make a histogram. Edit the settings in the histogram visualization dashboard to produce something similar to this:

_images/histogram.png

This shows the distribution of intensities for the image as a whole (gray), and for the subset in red (the label PRIMARY comes from the FITS header)

Perhaps we wish to remove faint pixels from our selection. To do this, we first enable the selection mode toolbar via Toolbars->Selection Mode Toolbar, and then pick the last mode (Remove From Selection mode).:

_images/modes.png

When this mode is active, new regions defined by the mouse are subtracted from the selected subsets. Thus, I can highlight the region between x=450-500 in the histogram to remove this region from the data.

_images/subset_refine.png

Note

Make sure you switch back to the first, default combination mode (Replace Selection mode)

Linking Data

Open w5_psc.vot – a catalog of Spitzer-identified point sources towards this region. You will see a new entry in the data manager.

_images/psc_layer.png

At this point, you can visualize and drilldown into this catalog. However, Glue doesn’t know enough to intercompare the catalog and image. To do that, we must Link these two data entries. Click on the Link Data button in the data manager. This brings up a new window, showing all the pieces of information within each dataset:

_images/link_editor.png

The image has an attribute Right Ascension. This is the same quantity as the RAJ2000 attribute in the w5_psc catalog – they are both describing Right Ascension (the horizontal spatial coordinate on the sky). Select these entries, and click Glue to instruct the program that these quantities are equivalent. Likewise, link Declination and DEJ2000 (Declination, the other coordinate). Click OK.

Note

What does this do? This tells Glue how to derive the catalog-defined quantities DEJ2000 and RAJ2000 using data from the image, and vice versa. In this case, the derivation is simple (it aliases the quantity Declination or Right Ascension). In general, the derivation can be more complex (i.e. an arbitrary function that maps quantities in the image to a quantity in the catalog). Glue uses this information to apply subset definitions to different data sets, overplot multiple datasets, etc.

After these connections are defined, subsets that are defined via spatial constraints in the image can be used to filter rows in the catalog. Let’s see how that works.

First, make a scatter plot of the point source catalog. Then, delete the West Arm subset (by highlighting it and clicking the X button). Then, highlight a new region in the image. You should see this selection applied to both plots:

_images/link_subset_1.png

You can also overplot the catalog rows on top of the image. To do this, click the arrow next to the new subset – this shows the individual selections applied to each dataset. Click and drag the subset for the point source catalog on top of the image. To see these points more easily, you may want to disable the selection applied to the image itself by unchecking the East arm (w5) entry in the plot layer window.

_images/link_subset_2.png

Glue is able to apply this filter to both datasets because it has enough information to apply the spatial constraint in the image (fundamentally, a constraint on Right Ascension and Declination) to a constraint in the catalog (since it could derive thsoe quantities from the RAJ2000 and DEJ2000 attributes).

Tip

Glue stores subsets as sets of constraints – tracing a rectangle subset on a plot defines a set of constraints on the quantities plotted on the x and y axes (left < x < right, bottom < y < top). Copying a subset copies this definition, and pasting it applies the definition to a different subset.

As was mentioned above, the highlighted subsets in the data manager are the ones which are affected by selecting regions in the plots. Thus, instead of manually copy-pasting subsets from the image to the catalog, you can also highlight both subsets before selecting a plot region. This will update both subsets to match the selection.

Note

Careful readers will notice that we didn’t use the image subset from earlier sections when working with the catalog. This is because that selection combined spatial constraints (the original rectangle in the image) with a constraint on intensity (the histogram selection). There is no mapping from image intensity to quantities in the catalog, so it isn’t possible to filter the catalog on that subset. In situations where Glue is unable to apply a filter to a dataset, it doesn’t render the subset in the visualization.

Saving your work

Glue provides a number of ways to save your work, and to export your work for further analysis in other programs.

Saving The Session

You can save a Glue session for later work via the File->Save Session menu. This creates a glue session file (the preferred file extension is .glu). You can restore this session later via File->Open Session.

By default, these files store references to the files you opened, and not copies of the files themselves. Thus, you won’t be able to re-load this session if you move any of the original data. To include the data in the session file, you can select ‘Glue Session including data’ when saving:

_images/save_with_data.png

Exporting the plots Glue can export certain kinds of plot combinations to other formats and web services.

Plot.ly is a cloud-based plot service whose features include the ability to tweak plot features (colors, annotations, etc.) through a GUI, and to easily share plots via web URLs. If your Glue session contains four or fewer scatter plots and/or histograms, these can be exported to a plotly page.

To do this, first sign up for a plotly account, and enter your user name and API key under File->Edit Settings. Then, select File->Export->Plotly. This will create a new plot, and open a browser window showing you the plot.

Exporting to D3PO

D3PO is an application created by Adrian Price Whelan, Josh Peek and others to create multi-stage “data stories”. Glue can export to the D3PO format under the following conditions:

  • Only scatterplots or histograms are used.
  • A single dataset is used.
  • Only one subset is visible within the viewers of each Glue tab.

Saving a session via File->Export->D3PO creates a directory with thee files that convert the Glue plots to a minimal D3PO page. Glue will also start a small webserver and open a browser window to show you the exported page.

Saving Plots Static images of individual visualizations can be saved by clicking the floppy disk icon on a given visualization window.

Saving Subsets Glue is primarily an exploration environment – eventually, you may want to export subsets for further analysis. Glue currently supports saving subsets as FITS masks. Right click on the subset in the data manager, and select Save Subset to write the subset to disk. This file will have the same shape as the original data, and will contain a 1/0 indicating whether that element is part of the subset.

User Interface Guide

How Data Linking Works

Glue makes it possible to compare different, interrelated datasets. For example, Glue allows you to:

  • Overlay scatterplots of the positions of objects in two different catalogs
  • Select a region of interest in an image, and use this spatial constraint to filter a catalog with position information
  • Overlay histograms that compare mass distributions of two different datasets.

To do this, Glue needs to understand how quantities in different datasets relate to each other:

  • Sometimes, two datasets define the same quantity (e.g., two catalogs that both report time)
  • Sometimes, datasets define the same quantities in different units (elapsed time in hours vs elapsed time in days)
  • Sometimes, a quantity (like area) can be derived from other quantites (like length and width).

Data Links tell Glue how to translate between different quantities, to intercompare different datasets.

Note

Are data links like table joins? If you are familiar with concepts from SQL, R, or Pandas, you might think data links are like data mergers or joins. They are different – mergers assume information about the same entity is present in many tables, such that the diffent tables can in principle be merged together. Data Links in glue, on the other hand, assume that the entrys in different datasets correspond to different entities, but may describe the same quantity. For example, an image and a position catalog both have spatial information, but no row in the catalog represents a pixel in the image. Data mergers are not yet supported in Glue.

Data Linking from the GUI

The Data Linking Editor let’s users define data links from the GUI.

_images/link_dialog.png

The simplest link occurs when two datasets define the same quantity in the same units. In this case, Glue can trivially overplot visualizations in both datasets. For example, in the image above, both datasets (a catalog and an image) both have the same RA and Dec spatial information (RA and Dec are essentially latitude and longitude coordinates on the sky). To link these quantities, we highlight the equivalent quantities, and click “Glue”.

_images/link_dialog_2.png

In the more general case, one quantity can be computed from one or more others, but is not identical to another quantity. The advanced tab let’s us specify how to use a translation function to derive one quantity from others:

_images/link_dialog_3.png

Here, a boxes dataset reports the linear dimensions of some boxes, and a crates dataset reports the volume of crates. The box volumes can be intercompared with the crate volumes by multiplying the box width, height, and depth. To specify this link, we select a translation function (lengths_to_volume), and drag the components to the relevant inputs and output of the translation function.

Note that this link is one-way: we can compute area from width height and depth, but not vice versa. Thus, we will be able to overlay information about box volume on a plot of crate volume, but not any information about crate height.

Merging Datasets

If several of your files describe the same items, you should generally merge them into a single Glue Data object.

Examples of files that make sense to merge together include:

  • 2 or more images that are pixel-aligned to each other
  • Several catalogs whose rows describe the same objects
Why merge?

For multi-dimensional visualizations (like a scatter plot, or an RGB image), merging datasets allows you to combine attributes from two different files into a single visualization. It also guarantees that any subset defined using attributes from one file can be applied to the entries in another file.

Merging vs Linking

Merging is a different operation than linking. The easiest way to appreciate the difference is to think of spreadsheet-like data. In Glue, linking two datasets defines a conceptual relationship between the columns of a spreadsheet (e.g., two spreadsheets have a column called “age”, but row N describes a different object in each spreadsheet).

Merging, on the other hand, indicates that two spreadsheets are pre-aligned along each row (e.g. row N describes the same item in every spreadsheet, but the columns of each spreadsheet might be different).

Merging collapses sevral datasets into a single dataset, while linking keeps each dataset separate.

How to merge datasets

Whenever you load a file whose shape matches a pre-existing dataset, Glue will ask you if you want to merge them into a single object. If you choose not to merge at this time, you can merge later by highlighting the relevant datasets in the left panel, right-clicking, and selecting Merge datasets.

To merge datasets programmatically, use the DataCollection.merge method.

Note

Datasets should only be merged if each element describes the same item in each file. Consequently, all merged datasets must have the same number of elements.

Defining New Components

New components of data items can be easily created from mathematical operations on existing components. In this section, we define new components for the W5 Point Source catalog from the tutorial.

Right-click on the w5_psc item in the Data Collection window and select Define new component:

_images/define_component_01.png

A new window will appear for defining components. Double-clicking on any of the Available Components will add it to the expression line. You can also type the name of the component – it will appear in blue if it is valid and in red if not, when separated by spaces from other parts of the expression. Here we define a new component __24__-__3.6_ to be the difference between 24 micron and 3.6 micron magnitudes:

_images/define_component_02.png

Remember to select the data item on the Add to window (here, w5_psc).

After clicking OK, the new component is available for plotting and other uses.

Furthermore, the expression line can include Numpy functions (prefaced with np.), and anything else you import in your config.py file for Glue. For example, if you wished to define a component expressing the 24 micron flux density in Janskys, you could use the np.power function:

_images/define_component_numpy.png

Spectrum Analysis

When using the image viewer on data with 3 or more dimensions, you have the option of extracting and analyzing spectra (or, more generally, integrated 1D profiles). To extract a spectrum, click the profile button on an image viewer:

_images/spectrum_button.png

Then, click-drag a box on the image. Glue will extract a spectrum by integrating over the dimensions of the box, for each slice of the cube. Likewise, you can also drag a subset onto the spectrum window, to extract a spectrum for the pixels in that subset.

The spectrum is displayed in a new window, which you can interact with in a few ways.

_images/spectrum_window.png
Interaction Modes
Cube Collapse

Click on the options button of the spectrum window, and then select the collapse tab. This allows you to partially collapse the cube, and send the result back to the image viewer. The two-sided handle on the plot defines the slices to collapse over, which you can edit by dragging the edges.

Profile Fitting

By clicking on the fit tab, you can fit a model to the extracted spectrum. Again, the two sided handle on the plot defines the range of data to fit. Clicking the fit button will add a best-fit model to the plot. The dropdown lets you choose which model to fit to the data.

Different models have different settings, which you can adjust by clicking on the settings button. For example, the (astropy-powered) Gaussian fitter allows you to fix certain parameters, or limit them to specific ranges.

Custom fitting plugins

The profile fitting tool is designed to be easily extended, so that you can plug in your own model fitting code worrying about GUI code. We will walk through several examples of custom fitting plugins, to demonstrate the various features of the plugin system.

Simple line fitter

Our first example is a simple linear model. Here’s the code:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
from glue.core.fitters import BaseFitter1D
from glue.config import fit_plugin
import numpy as np


@fit_plugin
class LineFit(BaseFitter1D):
    label = "Line"

    def fit(self, x, y, dy, constraints):
        return np.polyfit(x, y, 1)

    def predict(self, fit_result, x):
        return np.polyval(fit_result, x)

Let’s look at this line by line:

Line 6 wraps a subclass of BaseFitter1D in the fit_plugin decorator. All plugins follow this basic structure.

Line 8 gives this class a label, which is used in the GUI to label this model in the model selection dropdown.

Line 10 overrides the fit() method. All plugins must implement fit, which takes at least 4 parameters:

  • x: A numpy array of X values
  • y: A numpy array of Y values
  • dy: A numpy array of the errors on each Y value, or none
  • constraints: A dictionary of constraints (more on this later)

The fit method can do whatever it wants. Here, we are using numpy.polyfit() to fit a 1st-order polynomial to the data. We ignore dy and constraints. We return the result from polyfit – Glue doesn’t care what fit returns, it just passes that to other methods (as we will now see)

Line 13 overrides the predict() method. Again, all models must define this method. It takes 2 inputs – whatever was returned from fit(), and an array of X values to evaluate the model at. This method must return a array of model-predicted Y values at each X location. We use numpy.polyval() to do this

This code is enough to let us fit lines to data:

_images/line_screenshot.png

Note

In order for Glue to find this code, we need to copy this file to the same directory as config.py (~/.glue by default), and add import line_fit_plugin to config.py.

Polynomial fitter, with Options

Generalizing the line fitter above to higher degree polynomials is trivial, since polyfit/polyval both handle this case. We might want to make the degree of the fit a user-settable parameter. We can do this by adding a UI option, and a few keywords to our class:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
from glue.core.fitters import BaseFitter1D
from glue.core.simpleforms import IntOption
from glue.config import fit_plugin
import numpy as np


@fit_plugin
class PolynomialFitter(BaseFitter1D):
    label = "Polynomial"
    degree = IntOption(min=0, max=5, default=3, label="Polynomial Degree")

    def fit(self, x, y, dy, constraints, degree=2):
        return np.polyfit(x, y, degree)

    def predict(self, fit_result, x):
        return np.polyval(fit_result, x)

    def summarize(self, fit_result, x, y, dy=None):
        return "Coefficients:\n" + "\n".join("%e" % coeff
                                             for coeff in fit_result.tolist())

This code adds a few new features:

Line 10 adds an IntOption named degree to the class. Likewise, the fit method takes a keyword named degree, and uses this to fit a polynomial of order degree (e.g., degree=2 corresponds to a parabola). This extra information allows Glue to add a widget to the settings window:

_images/poly_screenshot.png

This plugin also overrides the summarize() method. Summarize returns a string, which is used as the display in the fit summary window.

Model with constraints

Models like those found in astropy.modeling support fixing or constraining certain parameters. If you would like to add user-setttable constraints to your model, add a param_names list to the class:

class ConstrainedGaussian(BaseFitter1D):
    param_names = ['amplitude']
    ...

Glue uses this information to let the user fix or limit parameters in the settings tab. This information is passed to the constraints argument of fit(). constraints is a dictionary whose keys are parameter names. Each value is itself a dictionary with 4 entries:

  • The default value of the parameter, or None if not set by the user
  • fixed, which is True if the parameter should be held fixed
  • limits, which is None if the value is unconstrained, or a list of minimum/maximum allowed values
Astropy-based models

The AstropyFitter1D base class can be subclassed to plug custom astropy models and fitters into Glue. This is very easy:

from astropy.modeling import models, fitting

@fit_plugin
class Gaussian(AstropyFitter1D):
    model_cls = models.Gaussian1D
    fitting_cls = fitting.NonLinearLSQFitter
    label = "Gaussian"

    def parameter_guesses(self, x, y, dy):
        return dict(amplitude=1, stddev=1, mean=1)

The parameter_guesses() method is optional, and provides initial guesses for the model parameters if they weren’t set by the user.

Custom Plotting

Fit plugins can also override the plot() method, to customize how the model fit is drawn on the profile.

Example: Gaussian fitting with Emcee

The emcee plugin example combines many of these ideas.

_images/emcee_screenshot.png

Slice Extraction

When visualizing image cubes, Glue’s image viewer extracts axis-parallel slices through the data. You can also extract slices from arbitrary paths through the data, using the slice tool in the image viewer:

_images/glue_slice.png

Activate this mode and click (or click+drag) a path on an image:

_images/galaxy_slice.png

Hitting escape will reset the path. Hitting enter will extract this slice from the original cube, and display it in a new window:

_images/galaxy_slice_extracted.png

The slice plot is linked to the original image viewer (of course!), so that click+dragging on the slice window will update the orientation of the image window.

This video demonstrates the process, and also shows the power of combining slice extraction and spectrum extraction.


This kind of slice extraction is especially useful for spectral cube analysis, since the extracted images are position-velocity diagrams. However, they can be useful in other contexts as well. For example, here’s a screenshot showing an on-the-fly cross section of a brain tumor MRI:


The script used to load this data into Glue can be found here.

Visualizing Astronomical Dendrograms

You can use Glue to visualize dendrograms created by the astrodendro package.

Enabling the viewer

The dendrogram viewer will be automatically available if the astrodendro package is installed. To install it, simply use:

pip install astrodendro
Building a dendrogram

The details of constructing dendrograms for astronomical images is beyond the scope of this document – see http://dendrograms.org/ for more information. The following snippet builds a dendrogram from the W5 image used in the tutorial:

from astropy.io import fits
from astrodendro import Dendrogram

data = fits.getdata('W5.fits')
dg = Dendrogram.compute(data, min_value=500, min_npix=50)
dg.save_to('w5_dendro.fits')

Next, load this file into Glue, choosing “Denrdogram” as a file type. You can now visualize the W5 dendrogram alongside its image:

_images/dendro.png
Linking to Catalog Properties

If you have used astrodendro to compute a catalog of structure properties, you can visualize that in Glue as well. The best way to do this is to save the catalog as a table, load it into Glue, and merge it with the dendrogram dataset. This will supplement the dendrogram with the additional catalog-derived properties.

Interacting with data from Python

Using the IPython terminal in Glue

Glue includes a button to open an IPython terminal window:

_images/ipython_button.png

This gives you programmatic access to Glue data. A number of variables are available by default (these are also listed when you open the terminal):

  • dc / data_collection refer to the central DataCollection, which is an object that holds all of the datasets, subsets, and data links
  • hub is the main communication hub.
  • application is the top level GlueApplication, which has access to plot windows (among other things)

Additionally, you can drag datasets and subsets into the terminal window, to easily assign them new variable names.

Most of your interactions will likely be with data objects and the data collection, so let’s take a look at these in the next section!

Working with Data objects

If you are using the IPython terminal in the Glue application, or if you are writing Python code that uses Glue, you will probably want to interact with data.

Data classes

The core data container in Glue is the Data class. Each Data instance can include any number of n-dimensional components, each represented by the Component class. The actual data resides in the Component objects. Because of this structure, a Data object can represent either a table, which is a collection of 1-d Component objects, or an n-dimensional dataset, which might include one (but could include more) n-dimensional Component objects.

Inside Data objects, each Component is assigned a ComponentID. However, this is not necesarily a unique ID for each and every component – instead, different components representing the same conceptual quantity can be given the same component ID. Component IDs are central to the linking framework

When using the Glue application, the Data objects are collected inside a DataCollection.

We can represent this graphically like this:

Glue Hierarchy

The DataCollection can be accessed by the dc variable in the IPython terminal. If the Glue application is not open, you can also create your own DataCollection by doing:

>>> from glue.core import DataCollection
>>> dc = DataCollection()

In the remainder of this page, we are going to assume that you have just followed the Getting Started tutorial, and have the Astronomy data on the W5 region loaded.

Alternatively, for the purposes of this tutorial, you can also load the same data manually into a Python/IPython session using:

>>> from glue.core import DataCollection
>>> from glue.core.data_factories import load_data
>>> dc = DataCollection()
>>> dc.append(load_data('getting_started/w5.fits'))
>>> dc.append(load_data('getting_started/w5_psc.vot'))

This sets up the dc object to be the same as what it would be in the Getting Started tutorial.

Using Data and DataCollection

Let’s take a look at the DataCollection:

>>> dc
DataCollection (2 data sets)
      0: w5[PRIMARY]
      1: w5_psc

DataCollection behaves like a list – you can access Data objects by indexing them. Let’s grab the first data object:

>>> data = dc[0]
>>> data
Data (label: w5[PRIMARY])
>>> data.components
[Declination, PRIMARY, Pixel x, Pixel y, Right Ascension]

Data objects behave like dictionaries: you can retrieve the numerical data associated with each one with bracket-syntax:

>>> data['PRIMARY']
array([[ 454.47747803,  454.18780518,  454.56842041, ...,  450.08349609,
         451.14971924,  450.25921631],
       ...,
       [ 442.0128479 ,  442.54266357,  443.43310547, ...,  441.5506897 ,
         442.89486694,  442.76904297]], dtype=float32)

Numpy-style fancy-indexing is also supported:

>>> data['PRIMARY', 0:3, 0:2]
array([[ 454.47747803,  454.18780518],
       [ 452.36376953,  452.8883667 ],
       [ 451.77172852,  453.42767334]], dtype=float32)

This is equivalent to:

>>> data['PRIMARY'][0:3, 0:2]
array([[ 454.47747803,  454.18780518],
       [ 452.36376953,  452.8883667 ],
       [ 451.77172852,  453.42767334]], dtype=float32)

Note that the indexing syntax (e.g. ['PRIMARY']) gives you the Numpy array, and not the Component object itself. The Numpy array is usually what you are interested in. However, you can retrieve the Component object if you like. To do this, you will first need to get the ComponentID for the component you are interested in::

>>> primary_id = data.components[0]
>>> primary_id
Declination
>>> type(primary_id)
glue.core.component_id.ComponentID

You can then fetch the component with get_component():

>>> component = data.get_component(primary_id)
>>> component.data
array([[ 58.84943461,  58.84956411,  58.84969336, ...,  58.84969336,
         58.84956411,  58.84943461],
       ...,
       [ 61.84155834,  61.84170457,  61.84185052, ...,  61.84185052,
         61.84170457,  61.84155834]])

Note

The item access syntax (square brackets) will not work if component labels are not unique. In this case, you must first retrieve the wanted ComponentID and use it to get the component object.

Adding new attributes to datasets

A common task is to combine two or more attributes in a dataset, and store the result as a new attribute to visualize. To demonstrate this, let’s use the W5 catalog data:

>>> dc
DataCollection (2 data sets)
      0: w5[PRIMARY]
      1: w5_psc
>>> catalog = dc[1]

We can examine the attributes in this dataset

>>> print(catalog)
Data Set: w5_psc
Number of dimensions: 1
Shape: 17771
Components:
 0) ID
 1) Pixel Axis 0
 2) World 0
 3) RAJ2000
 4) DEJ2000
 5) Jmag
 6) Hmag
 7) Ksmag
 ...

As mentioned in Using Data and DataCollection, Data objects behave like dictionaries mapping component names to numpy arrays. So one way to define a new component is like this:

>>> j_minus_h = catalog['Jmag'] - catalog['Hmag']
>>> catalog['jmh'] = j_minus_h

If you are using the Glue application, this new attribute is immediately available for visualizing.

Using lazy attributes

In the procedure above, the j_minus_h array was precomputed. An alternative approach is to define a new attribute that gets evaluated on-the-fly. While data[attribute_name] returns a numpy array, data.id[attribute_name] returns a ComponentID, which you can use as a lightweight proxy object that you can use to build simple arithmetic expressions:

>>> jmh_lazy = catalog.id['Jmag'] - catalog.id['Hmag']
>>> jmh_lazy
<BinaryComponentLink: (Jmag - Hmag)>
>>> catalog['jmh2'] = jmh_lazy

This new component is computed as needed on the fly, and can be more memory efficient for particular applications.

Defining new subsets

You can define new subsets from Python. An example might look like:

>>> state = catalog.id['Jmag'] > catalog.id['Hmag']
>>> label = 'J > H'
>>> subset_group = dc.new_subset_group(label, state)

If you using the Glue application, you can then change the visual properties of this subset using:

>>> sg.style.color = '#00ff00'
..note:: subset_group is not technically a subset, but a group of subsets.
This is beyond the scope of this tutorial, and explained in more detail in The selection/subset framework

This method of creating subsets can be a powerful technique. For a demo of using sending Scikit-learn-identified clusters back into Glue as subsets, see this notebook.

The following example demonstrates how to access subsets defined graphically in data viewers. Let’s say that you have two subsets that you defined in the scatter plot and histogram data viewers:

_images/subset_01.png

We’ll assume that you made a similar selection to these for demonstration purposes. You can now access the subsets from the built-in IPython console. To do this, assuming you have two subsets defined, you can do:

>>> red, faint_h = dc.subset_groups

Let’s also grab a component in the data:

>>> hmag = catalog.id['Hmag']

To find the intersection of the two subsets we have already defined (i.e., red sources with faint H band magnitudes):

>>> new_state = red & faint_h
>>> label = "Red and faint"
>>> data_collection.new_subset_group(label=label, subset_state=new_state)

The resulting intersection is shown in blue here:

_images/subset_02.png

The boolean operators &, ^, |, and ~ act on subsets to define new subsets represented by the intersection, exclusive union, union, and inverse, respectively.

You can also build subsets out of inequality constraints on component IDs:

>>> mid_mag = (hmag > 10) & (hmag < 15)
>>> dc.new_subset_group('between_10_15', mid_mag)

This selects objects with H band magnitudes between 10 and 15:

_images/subset_03.png

Accessing subset data

Once you have defined subsets, you can acceess the subsets on specific datasets using the .subsets attribute on Data objects. For instance, after the above selections, you might have something that looks like this:

>>> catalog.subsets
(Subset: between_10_15 (data: w5_psc), Subset: J > H (data: w5_psc))

Let’s access the first subset:

>>> subset = catalog.subsets[0]
>>> subset
Subset: between_10_15 (data: w5_psc)

You can access components of the subset as if it was a dataset:

>>> subset['Jmag']
array([ 15.34000015,  10.89999962,  13.30000019, ...,  13.06000042,
        13.38000011,  14.18000031], dtype=float32)

In this case, only the values in the selection are returned. If you prefer, you can also retrieve the subset as a boolean mask that can be applied to the original dataset:

>>> subset.to_mask()
Out[65]: array([ True,  True,  True, ...,  True,  True,  True], dtype=bool)

Creating a data object

In the above examples, we have assumed that the data objects were loaded via the Glue application. The readers/writers in Glue can also be accessed using the functions in glue.core.data_factories:

>>> from glue.core.data_factores import (load_data, gridded_data,
...                                      tabular_data)
>>> load_data('image.fits', factory=gridded_data)  # reads a FITS image
>>> load_data('catalog.csv', factory=tabular_data) # reads a catalog
>>> load_data('catalog.csv')  # guesses format

If these functions do not fit your needs, you can also write your own data loader, and use it from the Glue GUI.

It is also possible to create Data objects completely manually:

>>> from glue.core import Data
>>> data = Data(x=[1, 2, 3], y=[2, 3, 4], label="first dataset")

The arguments to the class are the components you want to create, as well as a label/name for the dataset. Each component can be given using the name=values syntax. The above example creates a Data with two components x and y.

You can then add the data object to the data collection using:

>>> dc.append(data)

Starting Glue from Python

In addition to using Glue as a standalone program, you can import glue as a library from Python. There are (at least) two good reasons to do this:

  1. You are working with multidimensional data in python, and want to use Glue for quick interactive visualization.
  2. You find yourself repeatedly loading the same sets of data each time you run Glue. You want to write a startup script to automate this process.

Quickly send data to Glue with qglue

The easiest way to send python variables to Glue is to use qglue():

from glue import qglue

For example, say you are working with a Pandas DataFrame:

>>> df
<class 'pandas.core.frame.DataFrame'>
Int64Index: 500 entries, 0 to 499
Data columns (total 3 columns):
x    500  non-null values
y    500  non-null values
z    500  non-null values
dtypes: float64(3)

You can easily start up Glue with this data using:

>>> qglue(xyz=df)

This will send this data to Glue, and label it xyz.

qglue() accepts many data types as inputs. Let’s see some examples:

import numpy as np
import pandas as pd
from astropy.table import Table

x = [1, 2, 3]
y = [2, 3, 4]

u = [10, 20, 30, 40]
v = [20, 40, 60, 80]

pandas_data = pd.DataFrame({'x': x, 'y': y})
dict_data = {'u': u, 'v': v}
recarray_data = np.rec.array([(0, 1), (2, 3)],
                             dtype=[('a', 'i'), ('b', 'i')])
astropy_table = Table({'x': x, 'y': y})
bad_data = {'x': x, 'u':u}
  • qglue(xy=pandas_data):

    constructs a dataset labeled xy, with two components (x and y)

  • qglue(uv=dict_data):

    construct a dataset labeled uv, with two components (u and v)

  • qglue(xy=pandas_data, uv=dict_data):

    constructs both of the previous two data sets.

  • qglue(rec=recarray_data, astro=astropy_table):

    constructs two datasets: rec (components a and b), and astro (components x and y)

  • qglue(bad=bad_data):

    doesn’t work, because the two components x and u have different shapes.

Note

Reminder: in Glue, Data sets are collections of one or more Component objects. Components in a dataset are bascially arrays of the same shape. For more information, see Working with Data objects

Note

Datasets cannot be given the label links.

Linking data with qglue

The Data Linking tutorial discusses how Glue uses the concept of links to compare different datasets. From the GUI, links are defined using the Link Manager. It is also possible to define some of these links with qglue.

The links keyword for qglue accepts a list of link descriptions. Each link description has the following format:

(component_list_a, component_set_b, forward_func, back_func)
  • component_list_a and component_list_b are lists of component names. In the first example above, the x component in the xyz dataset is named 'xyz.x'.
  • forward_func is a function which accepts one or more numpy arrays as input, and returns one or more numpy arrays as output. It computes the quantities in component_set_b, given the quantities in component_list_a.
  • back_func performs the reverse operastion.

Here’s an example:

def pounds_to_kilos(lbs):
    return lbs / 2.2

def kilos_to_pounds(kilos):
    return kilos * 2.2

def lengths_to_area(width, height):
    return width * height

link1 = (['data1.m_lb'], ['data_2.m_kg'], pounds_to_kilos, kilos_to_pounds)
link2 = (['data1.width', 'data1.height'], ['data2.area'], lengths_to_area)
qglue(data1=data1, data2=data2, links=[link1, link2])

The first link converts between the masses in two different data sets, recorded in different units. The second link is a 1-way link that computes the area of items in dataset 1, based on their width and height (there is no way to compute the width and height from the area measurements in dataset 2, so the reverse function is not provided). These links would enable the following interaction, for example:

  1. Overplot histograms of the mass distribution of both datasets
  2. Define a region in a plot of mass vs area for data 2, and apply that filter to dataset 1

Note

If you start Glue from a non-notebook IPython session, you will encounter an error like Multiple incompatible subclass instances of IPKernelApp are being created. The solution to this is to start Glue from a non-IPython shell, or from the notebook (see next section).

Using qglue with the IPython Notebook

You can call qglue() from the IPython notebook normally. However, the default behavior is for Glue to block the execution of the notebook while the UI is running. If you would like to be able to use the notebook and Glue at the same time, run this cell before starting glue:

%gui qt

This must be executed in a separate cell, before starting Glue.

Manual data construction

If qglue is not flexible enough for your needs, you can build data objects using the general Glue data API described in Working with Data objects.

Here’s a simple script to load data and pass it to Glue:

from glue.core.data_factories import load_data
from glue.core import DataCollection
from glue.core.link_helpers import LinkSame
from glue.app.qt.application import GlueApplication

#load 2 datasets from files
image = load_data('w5.fits')
catalog = load_data('w5_psc.vot')
dc = DataCollection([image, catalog])

# link positional information
dc.add_link(LinkSame(image.id['World x: RA---TAN'], catalog.id['RAJ2000']))
dc.add_link(LinkSame(image.id['World y: DEC--TAN'], catalog.id['DEJ2000']))

#start Glue
app = GlueApplication(dc)
app.start()

Some remarks:

  • load_data() constructs Glue Data objects from files. It uses the file extension as a hint for file type
  • Individual data objects are bundled inside a DataCollection
  • The LinkSame function indicates that two attributes in different data sets descirbe the same quantity
  • GlueApplication takes a DataCollection as input, and starts the GUI via start()

Starting Glue from a script

If you call glue with a python script as input, Glue will simply run that script:

$ glue startup_script.py

Likewise, if you are using the pre-built Mac application, you can right-click on a script and open the file with Glue.

Customizing/Hacking Glue

Configuring Glue via a startup file

Glue uses a configuration system to customize aspects such as which visualization modules it loads, what link functions to use, etc. This allows users who create their own glue modules to easily incorporate them into the main GUI environment.

The glue configuration file is called config.py. Glue looks for this file in the following locations, in order:

  • The current working directory
  • The path specified in the GLUERC environment variable, if present
  • The path .glue/config.py within the user’s home directory

To obtain a fresh config.py file to edit, run the command line program:

glue-config

Which will create a new file at ~/.glue/config.py

Customizing your Glue environment

Using a config.py file as described in Configuring Glue via a startup file, you can customize many aspects of your Glue environment, which are described in the following sections.

Registries

Before we talk about the different components of the Glue environment that you can customize, we first need to look at registries. Glue is written so as to allow users to easily register new data viewers, tools, exporters, and more. Registering such components can be done via registries located in the glue.config sub-package. Registries include for example link_function, data_factory, colormaps, and so on. As demonstrated below, some registries can be used as decorators (see e.g. Custom Link Functions) and for others you can add items using the add method (see e.g. Custom Colormaps).

In the following sections, we show a few examples of registering new functionality, and a full list of available registries is given in Complete list of registries.

Custom Data Loaders

Glue lets you create custom data loader functions, to use from within the GUI.

Here’s a quick example: the default image loader in Glue reads each color in an RGB image into 3 two-dimensional components. Perhaps you want to be able to load these images into a single 3-dimensional component called cube. Here’s how you could do this:

from glue.config import data_factory
from glue.core import Data
from skimage.io import imread

def is_jpeg(filename, **kwargs):
    return filename.endswith('.jpeg')

@data_factory('3D image loader', is_jpeg)
def read_jpeg(file_name):
    im = imread(file_name)
    return Data(cube=im)

Let’s look at this line-by-line:

  • The is_jpeg function takes a filename and keywords as input, and returns True if a data factory can handle this file
  • The @data_factory decorator is how Glue “finds” this function. Its two arguments are a label, and the is_jpeg identifier function
  • The first line in read_jpeg uses scikit-image to load an image file into a NumPy array.
  • The second line constructs a Data object from this array, and returns the result.

If you put this in your config.py file, you will see a new file type when loading data:

_images/custom_data.png

If you open a file using this file type selection, Glue will pass the path of this file to your function, and use the resulting Data object.

If you are defining a data factory that may clash with an existing one, for example if you are defining a loader for a specific type of FITS file, then make sure that the identifier function (e.g. is_jpeg above) returns True only for that specific subset of FITS files. Then you can set the priority= keyword in the @data_factory decorator. The value should be an integer or floating-point number, with larger numbers indicating a higher priority.

For more examples of custom data loaders, see the example repository.

Custom importers

The Custom Data Loaders described above allow Glue to recognize more file formats than originally implemented, but it is also possible to write entire new ways of importing data, including new GUI dialogs. An example would be a dialog that allows the user to query and download online data.

Currently, an importer should be defined as a function that returns a list of Data objects. In future we may relax this latter requirement and allow existing tools in Glue to interpret the data.

An importer can be defined using the @importer decorator:

from glue.config import importer
from glue.core import Data

@importer("Import from custom source")
def my_importer():
    # Main code here
    return [Data(...), Data(...)]

The label in the @importer decorator is the text that will appear in the Import menu in Glue.

Custom menubar tools

In some cases, it might be desirable to add tools to Glue that can operate on any aspects of the data or subsets, and can be accessed from the menubar. To do this, you can define a function that takes two arguments (the session object, and the data collection object), and decorate it with the @menubar_plugin decorator, giving it the label that will appear in the Tools menubar:

from glue.config import menubar_plugin

@menubar_plugin("Do something")
def my_plugin(session, data_collection):
    # do anything here
    return

The function can do anything, such as launch a QWidget, or anything else (such as a web browser, etc.), and does not need to return anything (instead it can operate by directly modifying the data collection or subsets).

Custom Colormaps

You can add additional matplotlib colormaps to Glue’s image viewer by adding the following code into config.py:

from glue.config import colormaps
from matplotlib.cm import Paired
colormaps.add('Paired', Paired)

Custom Subset Actions

You can add menu items to run custom functions on subsets. Use the following pattern in config..py:

from glue.config import single_subset_action

def callback(subset, data_collection):
    print "Called with %s, %s" % (subset, data_collection)

single_subset_action('Menu title', callback)

This menu item is available by right clicking on a subset when a single subset is selected in the Data Collection window. Note that you must select the subset specific to a particular Data set, and not the parent Subset Group.

Complete list of registries

A few registries have been demonstrated above, and a complete list of main registries are listed below. All can be imported from glue.config - each registry is an instance of a class, given in the second column, and which provides more information about what the registry is and how it can be used.

Registry name Registry class
qt_client glue.config.QtClientRegistry
tool_registry glue.config.QtToolRegistry
data_factory glue.config.DataFactoryRegistry
link_function glue.config.LinkFunctionRegistry
link_helper glue.config.LinkHelperRegistry
colormaps glue.config.ColormapRegistry
exporters glue.config.ExporterRegistry
settings glue.config.SettingRegistry
fit_plugin glue.config.ProfileFitterRegistry
single_subset_action glue.config.SingleSubsetLayerActionRegistry

Deferring loading of plug-in functionality (advanced)

In some cases, you may want to defer the loading of your component/functionality until it is actually needed. To do this:

  • Place the code for your plugin in a file or package that could be imported from the config.py (but don’t import it directly - it just has to be importable)
  • Include a function called setup alongside the plugin, and this function should contain code to actually add your custom tools to the appropriate registries.
  • In config.py, you can then add the plugin file or package to a registry by using the lazy_add method and pass a string giving the name of the package or sub-package containing the plugin.

Imagine that you have created a data viewer MyQtViewer. You could directly register it using:

from glue.config import qt_client
qt_client.add(MyQtViewer)

but if you want to defer the loading of the MyQtViewer class, you can place the definition of MyQtViewer in a file called e.g. my_qt_viewer.py that is located in the same directory as your config.py file. This file should look something like:

class MyQtViewer(...):
    ...

def setup():
    from glue.config import qt_client
    qt_client.add(MyQtViewer)

then in config.py, you can do:

from glue.config import qt_client
qt_client.lazy_add('my_qt_viewer')

With this in place, the setup in your plugin will only get called if the Qt data viewers are needed, but you will avoid unecessarily importing Qt if you only want to access glue.core.

Programmatically configuring plots

Plots in Glue are designed to be easily configured with Python. As much as possible, plot settings are controlled by simple properties on data viewer objects. For example:

from glue.core import Data, DataCollection
from glue.app.qt.application import GlueApplication
from glue.viewers.scatter.qt import ScatterWidget
import numpy as np

# create some data
d = Data(x=np.random.random(100), y=np.random.random(100))
dc = DataCollection([d])

# create a GUI session
ga = GlueApplication(dc)

# plot x vs y, flip the x axis, log-scale y axis
scatter = ga.new_data_viewer(ScatterWidget)
scatter.add_data(d)
scatter.xatt = d.id['x']
scatter.yatt = d.id['y']
scatter.xflip = True
scatter.ylog = True

# show the GUI
ga.start()

Plot Options

Here are the settings associated with each data viewer:

Scatter Plots
xlog log scaling on x axis?
ylog log scaling on y axis?
xflip invert the x axis?
yflip invert the y axis?
xmin Lower x limit of plot
xmax Upper x limit of plot
ymin Lower y limit of plot
ymax Upper y limit of plot
hidden Show hidden attributes
xatt Attribute to plot on x axis
yatt Attribute to plot on y axis
Image Viewer
data Current data
attribute Current attribute
rgb_mode RGB Mode?
slice
Histogram Viewer
xmin Minimum value
xmax Maximum value
normed Normalized?
autoscale Autoscale view to histogram?
cumulative Cumulative?
nbins Number of bins
xlog Log-scale the x axis?
ylog Log-scale the y axis?

Customizing Plots with Matplotlib

If you want, you can directly manipulate the Matplotlib plot objects that underly Glue plots. This can be useful if you want to create static plots with custom annotation, styles, etc.

From the GUI

Open the IPython terminal window. The application.viewers variable is a list of lists of all the open plot windows. Each inner list contains the data viewers open on a single tab. Every viewer has an axes attribute, which points to a Matplotlib Axes object:

plot = application.viewers[0][0]
ax = plot.axes
ax.set_title('Custom title')
ax.figure.canvas.draw()  # update the plot
From a script

Save the current glue session via File->Save Session. You can reload this session programmatically as follows:

from glue.app.qt.application import GlueApplication
app = GlueApplication.restore('output.glu', show=False)
plot = app.viewers[0][0]
ax = plot.axes

Building Custom Data Viewers

_images/bball_3.png

Glue’s standard data viewers (scatter plots, images, histograms) are useful in a wide variety of data exploration settings. However, they represent a tiny fraction of the ways to view a particular dataset. For this reason, Glue provides a simple mechanism for creating custom visualizations using matplotlib.

Creating a custom data viewer requires writing a little bit of Matplotlib code but involves little to no GUI programming. The next several sections illustrate how to build a custom data viewer by example.

The Goal: Basketball Shot Charts

In Basketball, Shot Charts show the spatial distribution of shots for a particiular player, team, or game. The New York Times has a nice example.

There are three basic features that we might want to incorporate into a shot chart:

  • The distribution of shots (or some statistic like the success rate), shown as a heatmap in the background.
  • The locations of a particular subset of shots, perhaps plotted as points in the foreground
  • The relevant court markings, like the 3-point line and hoop location.

We’ll build a Shot Chart in Glue incrementally, starting with the simplest code that runs.

Shot Chart Version 1: Heatmap and plot

Our first attempt at a shot chart will draw the heatmap of all shots, and overplot shot subsets as points. Here’s the code:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
from glue import custom_viewer

from matplotlib.colors import LogNorm

bball = custom_viewer('Shot Plot',
                      x='att(x)',
                      y='att(y)')


@bball.plot_data
def show_hexbin(axes, x, y):
    axes.hexbin(x, y,
                cmap='Purples',
                gridsize=40,
                norm=LogNorm(),
                mincnt=1)


@bball.plot_subset
def show_points(axes, x, y, style):
    axes.plot(x, y, 'o',
              alpha=style.alpha,
              mec=style.color,
              mfc=style.color,
              ms=style.markersize)

Before looking at the code itself, let’s look at how it’s used. If you include or import this code in your config.py file, Glue will recognize the new viewer. Open this shot catalog, and create a new shot chart with it. You’ll get something that looks like this:

_images/bball_1.png

Furthermore, subsets that we define (e.g., by selecting regions of a histogram) are shown as points (notice that Tim Duncan’s shots are concentrated closer to the hoop).

_images/bball_2.png

Let’s look at what the code does. Line 5 creates a new custom viewer, and gives it the name Shot Plot. It also specifies x and y keywords which we’ll come back to shortly (spoiler: they tell Glue to pass data attributes named x and y to show_hexbin).

Line 11 defines a show_hexbin function, that visualizes a dataset as a heatmap. Furthermore, the decorator on line 10 registers this function as the plot_data function, responsible for visualizing a dataset as a whole.

Custom functions like show_hexbin can accept a variety of input arguments, depending on what they need to do. Glue looks at the names of the inputs to decide what data to pass along. In the case of this function:

  • Arguments named axes contain the Matplolib Axes object to draw with
  • x and y were provided as keywords to custom_viewer. They contain the data (as arrays) corresponding to the attributes labeled x and y in the catalog

The function body itself is pretty simple – we just use the x and y data to build a hexbin plot in Matplotlib.

Lines 19-25 follow a similar structure to handle the visualization of subsets, by defining a plot_subset function. We make use of the style keyword, to make sure we choose colors, sizes, and opacities that are consistent with the rest of Glue. The value passed to the style keyword is a VisualAttributes object.

Custom data viewers give you the control to visualize data how you want, while Glue handles all the tedious bookeeping associated with updating plots when selections, styles, or datasets change. Try it out!

Still, this viewer is pretty limited. In particular, it’s missing court markings, the ability to select data in the plot, and the ability to interactively change plot settings with widgets. Let’s fix that.

Shot Chart Version 2: Court markings

We’d like to draw court markings to give some context to the heatmap. This is independent of the data, and we only need to render it once. Just as you can register data and subset plot functions, you can also register a setup function that gets called a single time, when the viewer is created. That’s a good place to draw court markings:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
from glue import custom_viewer

from matplotlib.colors import LogNorm
from matplotlib.patches import Circle, Rectangle, Arc
from matplotlib.lines import Line2D

bball = custom_viewer('Shot Plot',
                      x='att(x)',
                      y='att(y)')


@bball.plot_data
def show_hexbin(axes, x, y):
    axes.hexbin(x, y,
                cmap='Purples',
                gridsize=40,
                norm=LogNorm(),
                mincnt=1)


@bball.plot_subset
def show_points(axes, x, y, style):
    axes.plot(x, y, 'o',
              alpha=style.alpha,
              mec=style.color,
              mfc=style.color,
              ms=style.markersize)


@bball.setup
def draw_court(axes):

    c = '#777777'
    opts = dict(fc='none', ec=c, lw=2)
    hoop = Circle((0, 63), radius=9, **opts)
    axes.add_patch(hoop)

    box = Rectangle((-6 * 12, 0), 144, 19 * 12, **opts)
    axes.add_patch(box)

    inner = Arc((0, 19 * 12), 144, 144, theta1=0, theta2=180, **opts)
    axes.add_patch(inner)

    threept = Arc((0, 63), 474, 474, theta1=0, theta2=180, **opts)
    axes.add_patch(threept)

    opts = dict(c=c, lw=2)
    axes.add_line(Line2D([237, 237], [0, 63], **opts))
    axes.add_line(Line2D([-237, -237], [0, 63], **opts))

    axes.set_ylim(0, 400)
    axes.set_aspect('equal', adjustable='datalim')

This version adds a new draw_court function at Line 30. Here’s the result:

_images/bball_3.png

Shot Chart Version 3: Widgets

There are several parameters we might want to tweak about our visualization as we explore the data. For example, maybe we want to toggle between a heatmap of the shots, and the percentage of successful shots at each location. Or maybe we want to choose the bin size interactively.

The keywords that you pass to custom_viewer() allow you to set up this functionality. Keywords serve two purposes: they define new widgets to interact with the viewer, and they define keywords to pass onto drawing functions like plot_data.

For example, consider this version of the Shot Plot code:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
from glue import custom_viewer

from matplotlib.colors import LogNorm
from matplotlib.patches import Circle, Rectangle, Arc
from matplotlib.lines import Line2D
import numpy as np

bball = custom_viewer('Shot Plot',
                      x='att(x)',
                      y='att(y)',
                      bins=(10, 100),
                      hitrate=False,
                      color=['Reds', 'Purples'],
                      hit='att(shot_made)')


@bball.plot_data
def show_hexbin(axes, x, y, style,
                hit, hitrate, color, bins):
    if hitrate:
        axes.hexbin(x, y, hit,
                    reduce_C_function=lambda x: np.array(x).mean(),
                    cmap=color,
                    gridsize=bins,
                    mincnt=5)
    else:
        axes.hexbin(x, y,
                    cmap=color,
                    gridsize=bins,
                    norm=LogNorm(),
                    mincnt=1)


@bball.plot_subset
def show_points(axes, x, y, style):
    axes.plot(x, y, 'o',
              alpha=style.alpha,
              mec=style.color,
              mfc=style.color,
              ms=style.markersize)


@bball.setup
def draw_court(axes):

    c = '#777777'
    opts = dict(fc='none', ec=c, lw=2)
    hoop = Circle((0, 63), radius=9, **opts)
    axes.add_patch(hoop)

    box = Rectangle((-6 * 12, 0), 144, 19 * 12, **opts)
    axes.add_patch(box)

    inner = Arc((0, 19 * 12), 144, 144, theta1=0, theta2=180, **opts)
    axes.add_patch(inner)

    threept = Arc((0, 63), 474, 474, theta1=0, theta2=180, **opts)
    axes.add_patch(threept)

    opts = dict(c=c, lw=2)
    axes.add_line(Line2D([237, 237], [0, 63], **opts))
    axes.add_line(Line2D([-237, -237], [0, 63], **opts))

    axes.set_ylim(0, 400)
    axes.set_aspect('equal', adjustable='datalim')

This code passes 4 new keywords to custom_viewer():

  • bins=(10, 100) adds a slider widget, to choose an integer between 10 and 100. We’ll use this setting to set the bin size of the heatmap.
  • hitrate=False adds a checkbox. We’ll use this setting to toggle between a heatmap of total shots, and a map of shot success rate.
  • color=['Reds', 'Purples'] creates a dropdown list of possible colormaps to use for the heatmap.
  • hit='att(shot_made)' behaves like the x and y keywords from earlier – it doesn’t add a new widget, but it will pass the shot_made data along to our plotting functions.

This results in the following interface:

_images/bball_4.png

Whenever the user changes the settings of these widgets, the drawing functions are re-called. Furthermore, the current setting of each widget is available to the plotting functions:

  • bins is set to an integer
  • hitrate is set to a boolean
  • color is set to 'Reds' or 'Purples'
  • x, y, and hit are passed as AttributeInfo objects (which are just numpy arrays with a special id attribute, useful when performing selection below).

The plotting functions can use these variables to draw the appropriate plots – in particular, the show_hexbin function chooses the binsize, color, and aggregation based on the widget settings.

Shot Chart Version 4: Selection

One key feature still missing from this Shot Chart is the ability to select data by drawing on the plot. To do so, we need to write a select function that computes whether a set of data points are contained in a user-drawn region of interest:

1
2
3
@bball.select
def select(roi, x, y):
    return roi.contains(x, y)

With this version of the code you can how draw shapes on the plot to select data:

_images/bball_5.png

Viewer Subclasses

The shot chart example used decorators to define custom plot functions. However, if your used to writing classes you can also subclass CustomViewer directly. The code is largely the same:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
from glue.viewers.custom.qt import CustomViewer

from glue.core.subset import RoiSubsetState

from matplotlib.colors import LogNorm
from matplotlib.patches import Circle, Rectangle, Arc
from matplotlib.lines import Line2D
import numpy as np


class BBall(CustomViewer):
    name = 'Shot Plot'
    x = 'att(x)'
    y = 'att(y)'
    bins = (10, 100)
    hitrate = False
    color = ['Reds', 'Purples']
    hit = 'att(shot_made)'

    def make_selector(self, roi, x, y):

        state = RoiSubsetState()
        state.roi = roi
        state.xatt = x.id
        state.yatt = y.id

        return state

    def plot_data(self, axes, x, y,
                  hit, hitrate, color, bins):
        if hitrate:
            axes.hexbin(x, y, hit,
                        reduce_C_function=lambda x: np.array(x).mean(),
                        cmap=color,
                        gridsize=bins,
                        mincnt=5)
        else:
            axes.hexbin(x, y,
                        cmap=color,
                        gridsize=bins,
                        norm=LogNorm(),
                        mincnt=1)

    def plot_subset(self, axes, x, y, style):
        axes.plot(x, y, 'o',
                  alpha=style.alpha,
                  mec=style.color,
                  mfc=style.color,
                  ms=style.markersize)

    def setup(self, axes):

        c = '#777777'
        opts = dict(fc='none', ec=c, lw=2)
        hoop = Circle((0, 63), radius=9, **opts)
        axes.add_patch(hoop)

        box = Rectangle((-6 * 12, 0), 144, 19 * 12, **opts)
        axes.add_patch(box)

        inner = Arc((0, 19 * 12), 144, 144, theta1=0, theta2=180, **opts)
        axes.add_patch(inner)

        threept = Arc((0, 63), 474, 474, theta1=0, theta2=180, **opts)
        axes.add_patch(threept)

        opts = dict(c=c, lw=2)
        axes.add_line(Line2D([237, 237], [0, 63], **opts))
        axes.add_line(Line2D([-237, -237], [0, 63], **opts))

        axes.set_ylim(0, 400)
        axes.set_aspect('equal', adjustable='datalim')

Valid Function Arguments

The following argument names are allowed as inputs to custom viewer functions:

  • Any UI setting provided as a keyword to glue.custom_viewer(). The value passed to the function will be the current setting of the UI element.
  • axes is the matplotlib Axes object to draw to
  • roi is the glue.core.roi.Roi object a user created – it’s only available in make_selection.
  • style is available to plot_data and plot_subset. It is the VisualAttributes associated with the subset or dataset to draw
  • state is a general purpose object that you can use to store data with, in case you need to keep track of state in between function calls.

UI Elements

Simple user interfaces are created by specifying keywords to custom_viewer() or class-level variables to CustomViewer subclasses. The type of widget, and the value passed to plot functions, depends on the value assigned to each variable. See custom_viewer() for information.

Other Guidelines

  • You can find other example data viewers at https://github.com/glue-viz/example_data_viewers. Contributions to this repository are welcome!

  • Glue auto-assigns the z-order of data and subset layers to the values [0, N_layers - 1]. If you have elements you want to plot in the background, give them a negative z-order

  • Glue tries to keep track of the plot layers that each custom function creates, and auto-deletes old layers. This behavior can be disabled by setting viewer.remove_artists=False. Likewise, plot_data and plot_subset can explicitly return a list of newly-created artists. This might be more efficient if your plot is very complicated.

  • By default, plot_data and plot_subset are called whenever UI settings change. To disable this behavior, set viewer.redraw_on_settings_change=False.

  • By default, Glue sets the margins of figures so that the space between axes and the edge of figures is constant in absolute terms. If the default values are not adequate for your viewer, you can set the margins in the setup method of the custom viewer by doing e.g.:

    axes.resizer.margins = [0.75, 0.25, 0.5, 0.25]
    

    where the list gives the [left, right, bottom, top] margins in inches.

Watching data for changes

A new, experimental feature enables Glue to monitor the data files you’ve loaded for changes, and to auto-refresh plots when needed. This can be useful if your data update periodically, or if your data are produced by an analysis pipeline whose parameters you are iteratively refining.

To enable this feature, add the following line to your config.py script:

from glue.config import auto_refresh
auto_refresh(True)
_images/liveupdate.gif

Note

This currently only works if file updates do not change the shape of the underlying data.

Writing a fully customized Qt viewer (advanced)

Motivation

The custom_viewer() function and the CustomViewer class described in Building Custom Data Viewers are well-suited to developing new custom viewers that include some kind of Matplotlib plot. But in some cases, you may want to write a Qt data viewer that doesn’t depend on Matplotlib, or may use an existing widget. In this tutorial, we will assume that you have implemented a Qt widget that contains the functionality you want, and we will focus on looking at how to get it to work inside glue.

If you don’t already have an existing widget, but want to make sure it will work outside glue, start off by developing the widget outside of glue, then use the instructions below to make it usable inside glue.

Displaying the widget in glue

Let’s imagine that you have a Qt widget class called MyWidget the inherits from QWidget and implements a specific type of visualization you are interested in:

class MyWidget(QWidget):
    ...

Now let’s say we want to use this widget in glue, without having to change anything in MyWidget. The best way to do this is to create a new class, MyGlueWidget, that will wrap around MyWidget and make it glue-compatible. The glue widget should inherit from data_viewer (this class does a few boilerplate things such as, for example, adding the ability to drag and drop data onto your data viewer).

The simplest glue widget wrapper that you can write that will show MyWidget is:

from glue.qt.widgets.data_viewer import DataViewer

class MyGlueWidget(DataViewer):

    def __init__(self, session, parent=None):
        super(MyGlueWidget, self).__init__(session, parent=parent)
        self.my_widget = MyWidget()
        self.setCentralWidget(self.my_widget)

# Register the viewer with glue
from glue.config import qt_client
qt_client.add(MyGlueWidget)

If you put the contents above into a config.py file then launch glue in the same folder as the config.py file, you will then be able to go to the Canvas menu, select New Data Viewer, and you should then be presented with the window to select a data view, which should contain an ‘Override This’ entry:

_images/select_override.png

To give your viewer a more meaningful name, you should give your class an attribute called LABEL:

class MyGlueWidget(DataViewer):

    LABEL = "My first data viewer"

    def __init__(self, session, parent=None):
        super(MyGlueWidget, self).__init__(session, parent=parent)
        self.my_widget = MyWidget()
        self.setCentralWidget(self.my_widget)

Passing data to the widget

Now we want to be able to pass data to this viewer. To do this, you should define the add_data method which should take a single argument and return True if adding the data succeeded, and False otherwise. So for now, let’s simply return True and do nothing:

def add_data(self, data):
    return True

Now you can open glue again, and this time you should be able to load a dataset the usual way. When you drag this dataset onto the main canvas area, you will be able to then select your custom viewer, and it should appear (though the data itself will not). You can now expand the add_data method to actually add the data to MyWidget, by accessing self.my_widget, for example:

def add_data(self, data):
    self.my_widget.plot(data)
    return True

However, this will simply plot the initial data and plot more data if you drag datasets onto the window, but you will not for example be able to remove datasets, show subsets, and so on. In some cases, that may be fine, and you can stop at this point, but in other cases, if you want to define a way to interact with subsets, propagate selections, and so on, you will need to set up a glue client, which is discussed in Setting up a client. But first, let’s take a look at how we can add side panels in the dashboard which can include for example options for controlling the appearance or contents of your visualization.

Adding side panels

In the glue interface, under the data manager is an area we refer to as the dashboard, where different data viewers can include options for controlling the appearance or content of visualizations (this is the area indicated as C in :doc:getting-started). You can add any widget to the two available spaces.

In your wrapper class, MyGlueWidget in the example above, you will need to define a method called options_widget, which returns an instantiated widget that should be included in the dashboard on the bottom left of the glue window, and can contain options to control the data viewer.

For example, you could do:

class MyGlueWidget(DataViewer):

    ...

    def __init__(self, session, parent=None):
        ...
        self._options_widget = AnotherWidget(...)

    ...

    def options_widget(self):
        return self._options_widget

Note that despite the name, you can actually use the options widget to what you want, and the important thing is that options_widget is the bottom left pane in the dashboard on the left.

Note that you can also similarly define (via a method) layer_view, which sets the widget for the middle widget in the dashboard. However, this will default to a list of layers which can normally be used as-is (see Using Layers)

Setting up a client

Once the data viewer has been instantiated, the main glue application will call the register_to_hub method on the data viewer, and will pass it the hub as an argument. This allows you to set up your data viewer as a client that can listen to specific messages from the hub:

from glue.core.message import DataCollectionAddMessage

class MyGlueWidget(DataViewer):

    ...

    def register_to_hub(self, hub):

        super(MyGlueWidget, self).register_to_hub(hub)

        # Now we can subscribe to messages with the hub

        hub.subscribe(self,
                      DataUpdateMessage,
                      handler=self._update_data)

    def _update_data(self, msg):

        # Process DataUpdateMessage here

Using layers

By default, any sub-class of ~glue.viewers.common.qt.data_viewer will also include a list of layers in the central panel in the dashboard. Layers can be thought of as specific components of visualizations - for example, in a scatter plot, the main dataset will be a layer, while each individual subset will have its own layer. The ‘vertical’ order of the layers (i.e. which one appears in front of which) can then be set by dragging the layers around, and the color/style of the layers can also be set from this list of layers (by control-clicking on any layer).

Conceptually, layer artists can be used to carry out the actual drawing and include any logic about how to convert data into visualizations. If you are using Matplotlib for your visualization, there are a number of pre-existing layer artists in glue.viewers.*.layer_artist, but otherwise you will need to create your own classes.

The minimal layer artist class looks like the following:

from glue.core.layer_artist import LayerArtistBase

class MyLayerArtist(LayerArtistBase):

    def clear(self):
        pass

    def redraw(self):
        pass

    def update(self):
        pass

Essentially, each layer artist has to define the three methods shown above. The clear method should remove the layer from the visualization, the redraw method should redraw the entire visualization, and update, should update the apparance of the layer as necessary before redrawing.

In the data viewer, when the user adds a dataset or a subset, the list of layers should then be updated. The layers are kept in a list in the _layer_artist_container attribute of the data viewer, and layers can be added and removed with append and remove (both take one argument, which is a specific layer artist). So when the user adds a dataset, the viewer should do something along the lines of:

layer_artist = MyLayerArtist(data, ...)
self._container.append(layer_artist)
layer_artist.redraw()

If the user removes a layer from the list of layers by e.g. hitting the backspace key, the clear method is called, followed by the redraw method.

Getting help

Demo Videos

Quick Introduction to Glue (1 minute)

2013 SciPy Conference Talk (20 minutes)

The Perseus Shell (2 Minutes)

Featured in Alyssa Goodman’s plenary talk at the 223rd meeting of the American Astronomical Society

Glue, data cleaning, and civic hacking (5 minutes)

Glue, FBI Crime Data, and Plotly (5 minutes)

<

See also the IPython notebook that accompanies this video.

Extracting slices from cubes

Frequently Asked Questions

Why is Glue crashing on startup?

You might run into an error message like:

Qt internal error: qt_menu.nib could not be loaded.
The .nib file should be placed in QtGui.framework/Versions/Current/Resources/
or in the resources directory of your application bundle.

This due to a quirk in Anaconda that requires that python GUIs be run with pythonw instead of python. See Installing Glue for the latest instructions on how to properly install Glue with Anaconda.

What data formats does Glue understand?

Glue relies on several libraries to parse different file formats:

If Glue’s predefined data loaders don’t fit your needs, ou can also write your own loader, and plug it into Glue.

How do I overplot catalogs on images in Glue?

Take a look at this video. For more details, consult the tutorial.

How do I use Glue with the IPython notebook?

For IPython version >= 1.0, Glue can be started from the IPython HTML notebook without blocking, so that both the notebook and the Glue UI can run at once. This requires starting the notebook such that it cooperates with Qt. To do this, start the notebook with:

ipython notebook --gui qt

And then starting glue (probably by calling qglue) from the notebook.

If you encounter an error like this:

ValueError: API 'QString' has already been set to version 1

set the QT_API environment variable to either pyqt or pyside, depending on which version of Qt you have installed:

QT_API=pyside ipython notebook --gui qt

Does Glue Understand CASA Cubes?

Many radio astronomy datacubes are 4 dimensional, and give intensity as a function of 2 spatial dimensions, a frequency dimension, and a Stokes polarization dimension. By default, Glue will read these FITS images in as 4D hypercubes (use the task exportfits in CASA to write a native CASA image into a FITS cube that glue can read).

However, you might wish to load the image as a series of 3D cubes, one for each Stokes parameter. This would more easily allow you, for example, to compare histograms of intensities for each Stokes parameter.

Because of this, Glue has a special “CASA image loader”, to load these kinds of files. To open a file in this mode, select “CASA PPV Cube” in the file type dropdown when opening a file. This will create a single Data object with 1 attribute for each Stokes parameter.

Something is broken, or confusing. What should I do?

If you think you’ve found a bug in Glue, feel free to add an issue to the GitHub issues page. If you have general questions, feel free to post a message to the Glue mailing list, or send us an email directly.

You can also submit bug reports directly from Glue, by clicking Help->Send Feedback. This also sends us some diagnostic information about your Glue installation.

How do I make a scatterplot of columns from two different catalogs?

Merge the two datasets into a single object.

How do I make a scatterplot between two images?

Merge the two datasets into a single object, like you do for catalogs. This will only work if the two images have exactly the same shape (although it does not check if the WCS is the same). You can then drag the merged object into the visualization area to view an image of the first attribute. Repeat a second time to view the second image, and finally create a scatterplot comparing the image intensities at each pixel. You can then select regions in any of the three plots, and the corresponding selections in the other plots will be highlighted. Another interesting way to visualize your two related images is drop a 4th object using Image Viewer, but this time select the RGB instead of Monochrome, and put each image into one of the colors. Right mouse down will change the Contrast selected color after you have selected the bias/contrast button on the top row. Obviously with three images this is a great way to create a colorful RGB image.

A note on brushing in the image. If you want to just highlight a single pixel (pretty easy once you zoom in), you need to make sure your rectangular selection has its top right selection in that pixel. You cannot just highlight inside the pixel, it needs to cross boundaries between pixels.

_images/two_images.png

The intensity in image SUM1 and image SUM2 are correllated in the scatter plot. A second image of SUM1 is zoomed in around two warm pixels to see where they show up in the scatter plot (in yellow). In this particular diagram one can see that warm pixels follow the same relationship between SUM1 and SUM2 and the colder pixels.

I have some other question...?

Ask us a question on the mailing list!

Getting Help with Glue

If you have questions about Glue, we’d love to hear from you. There are several ways to get in touch:

  • Post a question on the Glue mailing list.
  • For specific bug reports, open a new issue on the GitHub page.
  • Send feedback from Glue itself by selecting Send Feedback from the help menu.

The Glue architecture

The pages below take you through the main infrastructure in Glue, and in particular how selections, linking, and communications are handled internally. You don’t need to understand all of this in order to get started with contributing, but in order to tackle some of the more in-depth issues, this will become important. This is not meant to be a completely exhaustive guide, but if there are areas that you feel could be explained better, or are missing and would be useful, please let us know!

The selection/subset framework

One of the central concepts in Glue is that of subsets, which are typically created as a result of the user selecting data in a viewer or creating the subset from the command-line. In order to go from a selection on the screen to defining a subset from a dataset, Glue includes the following concepts:

  • Region of interests (ROIs), which are an abstract representation of a geometrical region or selection.
  • Subset states, which is a descriptions of the subset selection.
  • Data Subsets, which are the result of applying a subset state/selection to a specific dataset.

When a user makes a selection in a data viewer in the Glue application, the selection is first translated into a ROI, after which the ROI is converted to a subset state, then applied to the data collection to produce subsets in each dataset. These three concepts are described in more detail below.

Regions of interest

The easiest way to think of regions of interest is as geometrical regions. Basic classes for common types of ROIs are included in the glue.core.roi sub-module. For example, the RectangularROI class describes a rectangular region using the lower and upper values in two dimensions:

>>> from glue.core.roi import RectangularROI
>>> roi = RectangularROI(xmin=1, xmax=3, ymin=2, ymax=5)

Note that this is not related to any particular dataset – it is an abstract representation of a rectangular region. It also doesn’t specify which components the rectangle is drawn in. All ROIs have a glue.core.roi.RectangularROI.contains() method that can be used to check if a point or a set of points lies inside the region:

>>> roi.contains(0, 3)
False
>>> roi.contains(2, 3)
True
>>> import numpy as np
>>> x = np.array([0, 2, 4])
>>> y = np.array([3, 3, 2])
>>> roi.contains(x, y)
array([False,  True, False], dtype=bool)

Subset states

While regions of interest define geometrical regions, subset states, which are sub-classes of SubsetState, describe a selection as a function of Glue ComponentID objects. Note that this is different from Subset instances, which describe the subset resulting from the selection (see Subsets). The following simple example shows how to easily create a SubsetState:

>>> from glue.core import Data
>>> data = Data(x=[1,2,3], y=[2,3,4])
>>> state = data.id['x'] > 1.5
>>> state
<InequalitySubsetState: (x > 1.5)>

Note that state is not the subset of values in data that are greater than 1.5 – instead, it is a representation of the inequality, the concept of selecting all values of x greater than 1.5. This distinction is important, because if another dataset defines a link between one of its components and the x component of data, then the inequality can be used for that other component too.

While the above syntax is convenient for using Glue via the command-line, in the case of data viewers, we actually want to translate ROIs into subset states. To do this, the Component class includes a subset_from_roi() method that takes a ROI and returns a subset state. At the moment this method works for 1- and 2-d ROIs. In the case of 2-d ROIs, the method should be given a reference to the second Component. In more complex cases, you can also define your own logic for converting ROIs into subset states. See the documentation of subset_from_roi() for more details.

Subset states can be combined using logical operations:

>>> state1 = data.id['x'] > 1.5
>>> state2 = data.id['y'] < 4
>>> state1 & state2
<glue.core.subset.AndState at 0x10ebd0160>
>>> state1 | state2
<glue.core.subset.OrState at 0x10ebd00f0>
>>> ~state1
<glue.core.subset.InvertState at 0x10ebd03c8>

Note that you should use &, |, and ~ as opposed to and, or, and not.

Subsets

A subset is what we normally think of as sub-part of a dataset. Subsets are typically created by making Subset states first. There are then different ways of applying this subset state to a Data object to actually create a subset. The easiest way of doing this is to simply call the new_subset() method with the SubsetState and optionally a label describing that subset:

>>> subset = data.new_subset(state, label='x > 1.5')
>>> subset
Subset: x > 1.5 (data: )

The resulting subset can then be used in a similar way to a Data object, but it will return only the values in the subset:

>>> subset['x']
array([2, 3])

>>> subset['y']
array([3, 4])

Finally, you can also get the mask from a subset:

>>> subset.to_mask()
array([False,  True,  True], dtype=bool)

One of the benefits of subset states is that they can be applied to multiple data objects, and if the different data objects have linked components (as described in The linking framework), this may produce several valid subsets in different datasets. We can apply a SubsetState to all datasets in a data collection by using the new_subset_group() method with the SubsetState and a label describing that subset, similarly to new_subset()

>>> from glue.core import DataCollection
>>> data_collection = DataCollection([data])
>>> subset_group = data_collection.new_subset_group('x > 1.5', state)

This creates a SubsetGroup which represents a group of subsets, with the individual subsets accessible via the subsets attribute:

>>> subset = subset_group.subsets[0]
>>> subset
Subset: x > 1.5 (data: )

The communication framework

Publish/Subscribe model

Glue is built around a publish/subscribe paradigm that allows individual components to remain synchronized without knowing about each other. The core object that allows this is the Hub, which listens for messages from various parts of Glue and relays messages to other interested objects about changes in state to the data and subsets.

You can instantiate a Hub instance directly:

>>> from glue.core import Hub
>>> hub = Hub()

but in most cases if you are using a DataCollection, you can let it instantiate the hub instead and access it via the .hub attribute:

>>> from glue.core import DataCollection
>>> data_collection = DataCollection()
>>> data_collection.hub
<glue.core.hub.Hub at 0x102991dd8>

Messages are exchanged using Message objects. A message is a notice that something interesting has happened. Various sub-classes of Message exist, such as DataMessage or SubsetMessage, and even more specialized ones such as DataCollectionAddMessage.

Using the subscribe() method, you can easily attach callback functions/methods to specific messages using the syntax:

hub.subscribe(self, subscriber, message_class, handler=..., filter=...)

where the message_class is the type of message to listen for, such as DataMessage, handler is the function/method to be called if the message is received (the function/method should take one argument which is the message), and filter can be used to specify conditions in which to pass on the message to the function/method (for more information on this, see the subscribe() documentation).

Subscribing to messages has to be done from a HubListener instance. The following simple example shows how to set up a basic HubListener and register to listen for DataMessage and DataCollectionAddMessage:

>>> from glue.core import Hub, HubListener, Data, DataCollection
>>> from glue.core.message import (DataMessage,
...                                DataCollectionMessage)
>>>
>>> class MyListener(HubListener):
...
...     def __init__(self, hub):
...         hub.subscribe(self, DataCollectionMessage,
...                       handler=self.receive_message)
...         hub.subscribe(self, DataMessage,
...                       handler=self.receive_message)
...
...     def receive_message(self, message):
...         print("Message received:")
...         print("{0}".format(message))

We can then create a data collection, and create an instance of the above class:

>>> data_collection = DataCollection()
>>> hub = data_collection.hub
>>> listener = MyListener(hub)

If we create a new dataset, then add it to the data collection created above, we then trigger the receive_message method:

>>> data = Data(x=[1,2,3])
>>> data_collection.append(data)
Message received:
DataCollectionAddMessage:
    Sent from: DataCollection (1 data set)
    0:

Note that DataCollectionAddMessage is a subclass of DataCollectionMessage – when registering to a message class, sub-classes of this message will also be received.

It is also possible to trigger messages manually:

>>> # We can also create messages manually
... message = DataMessage(data)
>>> hub.broadcast(message)
Message received:
DataMessage:
     Sent from: Data Set: Number of dimensions: 1
Shape: 3
Components:
 0) x
 1) Pixel Axis 0
 2) World 0

Typical workflow

This is used in Glue to produce the following communication workflow:

  • An empty DataCollection object is created, and automatically connected to a Hub.
  • Data are added to the data collection
  • Several clients register to the hub, and subscribe to particular types of messages.
  • Something (perhaps code, perhaps user interaction with a client) acts to change the state of a data or subset object. These changes automatically generate particular messages that get sent to the Hub. These messages communicate atomic events such as a change in the data, a change in a subset, or the fact a subset has been deleted.
  • Upon receiving a message, the hub relays it to all clients that have subscribed to that particular message type.
  • The clients react to the message however they see fit.

Here, we use the term client in the generic sense of a class that interacts with the hub. However, Glue does include a base Client class that pre-defines a number of useful connections for data viewers. Some of the data viewers make use of this class, although there is no obligation to do so in principle, provided the class subscribing to messages is a subclass of HubListener.

The linking framework

One of the strengths of Glue is the ability to be able to link different datasets together. The How Data Linking Works page describes how to set up links graphically from the Glue application, but in this page, we look at how links are set up programmatically.

Information on the Data framework is available in Working with Data objects and is not repeated here.

Developing Glue

Developer Guide

So you want to help develop Glue? Great, let’s get started! :)

First, be sure to join the glueviz-dev mailing list for any questions or discussions related to development, and let us know if any of the documentation below is unclear. You are also very welcome to introduce yourself on the list and let us know that you are interested in contributing!

If you are going to be developing Glue, we highly recommend that you familiarize yourself with the rest of the documentation, and in particular the The Glue architecture documentation, which describes in detail how some of the Glue internals work.

If you want to contribute, but don’t yet have a specific idea of where to make contributions, you can check out our Issue tracker. We use different labels to categorize the issues, and one label you might be interested in is package-novice, which highlights issues that don’t require an in-depth understanding of the Glue package, but only require you to understand/edit a small part of the code base (which is typically mentioned in the issue).

The following pages provide more logistical information about the layout of the code, coding and testing guidelines, and some more advanced topics:

Code organization

The Glue code base is intended to be organized in a modular way, such that you will never need to understand all the code in Glue, and the aim is for it to be easy for you to identify where to make specific changes to implement the functionality you need or fix issues.

Glue sub-packages

The code is organized into the following top-level sub-packages (starting with some of the easy ones):

glue.external

This is a sub-package that you should never have to edit directly. It contains files and modules edited in other repositories that have been bundled with Glue. If you do need to make any changes to them, you should first edit those other repositories, and then port over the changes to Glue. The most notable example is glue.external.qt, which is a bundled version of the qt-helpers. This sub-package provides a common interface for PyQt4, PyQt5, and PySide. See Using qt-helpers for more details. Another example is glue.external.echo, which is a bundled version of the echo library, which makes it easy to attach callback functions to class properties. In general, it’s useful to know these bundled modules are available, but you will likely not need to edit them.

glue.utils

This is a sub-package that contains various Python, Matplotlib, and Qt-related utilities that do not depend on any other parts of Glue. These utilities don’t know about Glue data objects, subsets, or specific data viewers. Instead, this sub-package includes utilities such as points_inside_poly(), a function to find whether points are inside a polygon, or cmap2pixmap(), a function to convert a Matplotlib colormap into a Qt QPixmap instance. This is one of the easiest sub-packages to approach – it is just a collection of small helper functions and classes and doesn’t require understanding any other parts of Glue.

glue.core

As its name describes, this is the most important part of the Glue package. This defines the general classes for datasets, subsets, data collections, messages, layer artists, and other Glue concepts. On the other hand it does not define specific viewers or data readers. The code in this sub-pacakge is not concerned with specific graphical user interface (GUI) representations, and you could in principle develop a completely different GUI than the main Glue one making use of the Glue core code. You could even use glue.core to give glue-like functionality to other existing applications.

glue.viewers

This sub-package contains the code for all the built-in viewers in glue, such as the scatter plot and image viewers. Each viewer is contained in a sub-package of glue.viewers, such as glue.viewers.scatter. A glue.viewers.common sub-package is also provided, with utilities and base classes that might be useful for various viewers. For instance, the glue.viewers.common.qt.mouse_mode sub-module contains code related to defining mouse ‘modes’ for selection.

glue.dialogs

This sub-package contains implementations of various common dialogs, each organized into sub-packages. For instance, glue.dialogs.custom_component contains the implementation of the dialog used to add new components to datasets in the Glue application. The implementation for these dialogs only uses the framework from the glue.core package and the dialogs don’t need to know anything about the rest of the state of the design of the Glue application.

glue.app

This package defines the Glue Application, that is the default GUI that users interact with if they launch the Glue Application. This essentially pulls together all the components from other sub-packages into a single application. However, it would be entirely possible to develop other applications using the available components - for instance, one could build an application with fixed data viewers for a specific purpose.

glue.plugins

This package features more specialized tools/viewers for Glue, and in the long term some of these will be moved into top-level sub-packages such as glue.viewers as they are made more general.

glue.icons

This contains various icons used in Glue, both in the vector SVG form, and in rasterized PNG format.

Qt-specific code

Glue currently uses the Qt GUI framework. However, this does not mean that you need to know Qt to understand all of the code in Glue. Instead, we have taken care to isolate all Qt-specific code into directories called qt/. For instance, the glue/utils/qt directory contains Qt-related utilities, and any other code in glue/utils is not allowed to import Qt. We enforce this while testing by making sure that all the tests in Glue run if all the qt/ directories are removed, and no Qt implementation is installed.

Another example is that the glue/viewers/scatter/qt directory contains code for the scatter plot viewer that is Qt-specific, but any other code in glue/viewers/scatter is Qt-agnostic. As a result, if you are trying to fix something that is not related to the GUI, but to e.g. the data structures in Glue, or the specific way in which e.g. Matplotlib displays something, you shouldn’t have to go into any of the qt sub-directories.

Another consequence of this is that if you or anyone else is interested in developing a GUI front-end for Glue that is not based on Qt, you can re-use a lot of the existing code that is not in the Qt directories. If we were to add the code for another GUI framework into the Glue package, we could simply create directories parallel to the qt directories but for the new framework.

Qt development in Glue

Using qt-helpers

If you are interested in working on some of the Qt-specific code, it’s important that you don’t import any code directly from PyQt4, PyQt5, or PySide. Since we want to maintain backward-compatibility with all of these, you should always import from glue.external.qt as if this was one of the Python Qt packages. For instance, instead of:

from PyQt4 import QtGui

you should do:

from glue.external.qt import QtGui

Note that for now, if the PyQt4 and PyQt5 import paths would be different, you should use the PyQt4 one, and we have provided patches in glue.external.qt to make PyQt5 backward-compatible.

Coding guidelines

Glue is written entirely in Python, and we abide by the following guidelines:

  • All code should be Python 2 and 3-compatible. We do this by using the six package, which we bundle in glue.external.six.
  • We follow many of the same guidelines as the Astropy project, which you can find here.
  • We use absolute imports for most of the code in Glue, with the exception of tests, which are allowed to import the classs/functions they are testing using relative imports. This means that if we need to move files and their associated tests around, the tests will still work without having to change the imports.
  • All Qt-specific code should live in qt/ sub-directories (see Qt-specific code for more details).
  • Docstrings should be written using the numpydoc format, which is described in detail here.

Testing framework

Writing tests

In order to make sure that everything works as expected, and that we don’t introduce any regressions when making changes, Glue includes a number of tests. All tests are contained inside the tests/ directories in each sub-package of Glue. For example, the tests for the glue.core functionality are in glue/core/tests. The tests for the glue.utils.qt functionality are in glue/utils/qt/tests. Tests should always live close to the code they are testing.

In general, we try and make tests as specific as possible to the code being tested - so for example if we create a new dialog, and we want to check that various widgets in the tests are working correctly, we don’t need to launch the whole Glue application. Instead, we can create a test data collection with some example data, and simply launch the dialog in question.

We run tests using py.test, and tests are therefore written using this framework. The syntax for a basic test is actually very simple and not py.test-specific – it is simply a function whose name starts with test_. If the function crashes, the test fails, and if the function runs without crashing, the test passes:

def test_functionality():
    # This is an example of a test that passes
    a = 1
    b = 2 + a
    assert a == 3

Some tests are also written using test classes instead of functions (which can include py.test-specific methods), and we can also make use of various decorators in py.test to e.g. parameterize tests.

Rather than provide a full guide on how to write tests, we encourage you to take a look at the Astropy documentation on Writing tests, since we follow the same guidelines as them.

Running tests

The easiest way to run the Glue tests is to do:

python setup.py test

You can also invoke py.test directly:

py.test glue

To run only a specific test, you can do:

py.test glue/core/tests/test_links.py
Continuous integration

Every time someone opens a pull request to the Glue repository, and every time we merge changes into the code base, all the tests are run on Travis and AppVeyor. This is referred to as Continuous Integration. One of the nice things about continuous integration is that it allows us to automatically run the tests for different operating systems, Python versions, versions of Numpy, and Qt frameworks (PyQt4, PyQt5, and PySide).

Travis runs tests on Linux and MacOS X, and AppVeyor runs the tests on Windows. When you open a pull request, you will be able to check the status of the tests at the bottom, which will look something like this:

_images/ci_status.png

In this example, the tests failed on Travis, but passed on Windows. You can then get more information about which set-ups failed by clicking on ‘Details’.

How the MacOS X app is built

While we recommend that you use Glue by installing the glueviz package in Conda, we also make available an all-in-one app for MacOS X users (see here for the most recent versions of the app).

When Travis runs (see Testing framework), one of the builds triggers the following script. If the Travis build is for a pull request or is not for the main Glue repository, the script exits early. However, if the test is for the master branch of the Glue repository, the script runs, and triggers a Travis build for the Travis-MacGlue repository.

This then sets up a Travis build that includes all the dependencies for Glue, and then runs py2app. Once this has completed successfully, the app file is uploaded to Amazon S3 and becomes available at mac.glueviz.org.

API

API

Core Data

glue.core.component Module
Classes
Component(data[, units]) Stores the actual, numerical information for a particular quantity
DerivedComponent(data, link[, units]) A component which derives its data from a function
CategoricalComponent(categorical_data[, ...]) Container for categorical data.
CoordinateComponent(data, axis[, world]) Components associated with pixel or world coordinates
glue.core.component_id Module
Classes
ComponentID(label[, hidden]) References a glue.core.component.Component object within a Data object.
ComponentIDDict(data, **kwargs)
glue.core.data Module
Classes
Data([label]) The basic data container in Glue.
glue.core.data_collection Module
Classes
DataCollection([data]) The top-level object for interacting with datasets in Glue.
glue.core.hub Module
Classes
Hub(*args) The hub manages communication between subscribers.
HubListener The base class for any object that subscribes to hub messages.
glue.core.subset Module
Functions
combine_multiple(subsets, operator)
Classes
Subset(data[, color, alpha, label]) Base class to handle subsets of data.
SubsetState()
RoiSubsetState([xatt, yatt, roi])
CategoricalROISubsetState([att, roi])
RangeSubsetState(lo, hi[, att])
MultiRangeSubsetState(pairs[, att]) A subset state defined by multiple discontinuous ranges
CompositeSubsetState(state1[, state2])
OrState(state1[, state2])
AndState(state1[, state2])
XorState(state1[, state2])
InvertState(state1[, state2])
MaskSubsetState(mask, cids) A subset defined by boolean pixel mask
CategorySubsetState(attribute, values)
ElementSubsetState([indices])
InequalitySubsetState(left, right, op)
glue.core.subset_group Module

A SubsetGroup unites a group of Subset instances together with a consistent state, label, and style.

While subsets are internally associated with particular datasets, it’s confusing for the user to juggle multiple similar or identical subsets, applied to different datasets. Because of this, the GUI manages SubsetGroups, and presents each group to the user as a single entity. The individual subsets are held in-sync by the SubsetGroup.

Client code should only create Subset Groups via DataCollection.new_subset_group. It should not call Data.add_subset or Data.new_subset directly

Classes
GroupedSubset(data, group) A member of a SubsetGroup, whose internal representation
SubsetGroup([color, alpha, label, subset_state]) Create a new empty SubsetGroup
glue.core.roi Module
Classes
Roi A geometrical 2D region of interest.
RectangularROI([xmin, xmax, ymin, ymax]) A 2D rectangular region of interest.
CircularROI([xc, yc, radius]) A 2D circular region of interest.
PolygonalROI([vx, vy]) A class to define 2D polygonal regions-of-interest
AbstractMplRoi(axes) Base class for objects which use
MplRectangularROI(axes) A subclass of RectangularROI that also renders the ROI to a plot
MplCircularROI(axes) Class to display / edit circular ROIs using matplotlib
MplPolygonalROI(axes) Defines and displays polygonal ROIs on matplotlib plots
MplXRangeROI(axes)
param axes:A matplotlib Axes object to attach the graphical ROI to
MplYRangeROI(axes)
param axes:A matplotlib Axes object to attach the graphical ROI to
XRangeROI([min, max])
RangeROI(orientation[, min, max])
param orientation:
 ‘x’ or ‘y’. Sets which axis to range
YRangeROI([min, max])
VertexROIBase([vx, vy])
param vx:initial x vertices
CategoricalROI([categories]) A ROI abstraction to represent selections of categorical data.
glue.core.data_factories Package
Functions
aastex_factory(file, **kwargs)
astropy_tabular_data(*args, **kwargs) Build a data set from a table.
auto_data(filename, *args, **kwargs) Attempt to automatically construct a data object
casalike_cube(filename, **kwargs) This provides special support for 4D CASA FITS - like cubes, which have 2 spatial axes, a spectral axis, and a stokes axis in that order.
cds_factory(file, **kwargs)
daophot_factory(file, **kwargs)
data_label(path) Convert a file path into a data label, by stripping out slashes, file extensions, etc.
find_factory(filename, **kwargs)
fits_reader(source[, auto_merge, ...]) Read in all extensions from a FITS file.
has_extension(exts) A simple default filetype identifier function
hdf5_reader(filename[, format, auto_merge]) Read in all datasets from an HDF5 file
img_data(file_name) Load common image files into a Glue data object
ipac_factory(file, **kwargs)
is_casalike(filename, **kwargs) Check if a FITS file is a CASA like cube,
is_fits(filename)
is_hdf5(filename)
is_npy(filename) The first bytes are: x93NUMPY
is_npz(filename) The first bytes are: x93NUMPY
latex_factory(file, **kwargs)
load_data(path[, factory]) Use a factory to load a file and assign a label.
npy_reader(filename[, format, auto_merge]) Read in a Numpy structured array saved to a .npy or .npz file.
npz_reader(filename[, format, auto_merge]) Read in a Numpy structured array saved to a .npy or .npz file.
pandas_read_table(path, **kwargs) A factory for reading tabular data using pandas
sextractor_factory(file, **kwargs)
tabular_data(path, **kwargs)
Classes
FileWatcher(path, callback[, poll_interval]) Watch a path for modifications, and perform an action on change
LoadLog(path, factory, kwargs) This class attaches some metadata to data created from load_data, so that the data can be re-constructed when loading saved state.
glue.core.fitters Module

Glue’s fitting classes are designed to be easily subclassed for performing custom model fitting in Glue.

See the guide on writing custom fit plugins for help with using custom fitting utilities in Glue.

Classes
BaseFitter1D(**params) Base class for 1D fitters.
PolynomialFitter(**params) A polynomial model.
AstropyFitter1D(**params) A base class for wrapping astropy.modeling.
SimpleAstropyGaussianFitter(**params) Guassian fitter using astropy.modeling.
BasicGaussianFitter(**params) Fallback Gaussian fitter, for astropy < 0.3.

User Interface

glue.core.application_base Module
Classes
Application([data_collection, session])
ViewerBase(session) Base class for data viewers in an application
glue.viewers.common.qt.data_viewer Module
Classes
DataViewer(session[, parent]) Base class for all Qt DataViewer widgets.
glue.viewers.scatter.qt.viewer_widget Module
Classes
ScatterWidget(session[, parent]) An interactive scatter plot.
glue.viewers.image.qt.viewer_widget Module
Classes
ImageWidget(session[, parent]) A matplotlib-based image widget
StandaloneImageWidget([image, wcs, parent]) A simplified image viewer, without any brushing or linking, but with the ability to adjust contrast and resample.
ImageWidgetBase(session[, parent]) Widget for ImageClient
glue.viewers.histogram.qt.viewer_widget Module
Classes
HistogramWidget(session[, parent])
glue.app.qt.application Module
Classes
GlueApplication([data_collection, session]) The main GUI application for the Qt frontend
glue.qglue Module

Utility function to load a variety of python objects into glue

Functions
qglue(**kwargs) Quickly send python variables to Glue for visualization.
glue.viewers.custom.qt.custom_viewer Module

This module provides utilities for creating custom data viewers. The goal of this module is to make it easy for users to make new data viewers by focusing on matplotlib visualization logic, and not UI or event processing logic.

The end user typically interacts with this code via glue.custom_viewer()

Classes
AttributeInfo An array subclass wrapping a Component of a dataset
ViewerState Empty object for users to store data inside
UserDefinedFunction(name) Descriptor to specify a UserDefinedFunction.
CustomViewer(widget_instance) Base class for custom data viewers.
SettingsOracleInterface
SettingsOracle(settings, **override)
CustomViewerMeta Metaclass to construct CustomViewer and subclasses
CustomSubsetState(viewer_cls, roi, settings) A SubsetState subclass that uses a CustomViewer’s “select” function
FrozenSettings(**kwargs) Encapsulates the current settings of a CustomViewer
CustomViewer(widget_instance) Base class for custom data viewers.
CustomArtist(layer, axes, coordinator) LayerArtist for custom viewers
CustomClient(*args, **kwargs)
CustomWidgetBase(session[, parent]) Base Qt widget class for custom viewers
FormDescriptor(name)
FormElement(params) Base class for user-defined settings in a custom widget.
NumberElement(params) A form element representing a number
LabeledSlider(min, max[, default, parent]) A labeled slider widget, that handles floats and integers
BoolElement(params) A checkbox representing a boolean setting
FixedComponent(params) An element for a Data Component.
ComponenentElement(params) A dropdown selector to choose a component
ChoiceElement(params) A dropdown selector to choose between a set of items
glue Package
Functions
custom_viewer(name, **kwargs) Create a custom interactive data viewer.
qglue(**kwargs) Quickly send python variables to Glue for visualization.
test([no_optional_skip])
glue.core.layer_artist Module

LayerArtist classes handle the visualization of an individual subset or dataset.

Visualization clients in Glue typically combose visualizations by stacking visualizations of several datasets and subsets on top of each other. They do this by creating and managing a collection of LayerArtists, one for each Data or Subset to view.

LayerArtists contain the bulk of the logic for actually rendering things

Classes
LayerArtistBase(layer) Create a new LayerArtist
MatplotlibLayerArtist(layer, axes) MPL-specific layer artist base class, that uses an Axes object
LayerArtistContainer() A collection of LayerArtists

Viewers

glue.viewers.common.viz_client Module
Classes
VizClient(data[, options]) The VizClient class provides an interface (and minimal implementation) for a generic client that creates visualizations.
GenericMplClient([data, figure, axes, ...]) This client base class handles the logic of adding, removing, and updating layers.

Utilities

glue.config Module
Classes
Registry() Container to hold groups of objects or settings.
SettingRegistry() Stores key/value settings that code can use to customize Glue
ExporterRegistry() Stores functions which can export an applocation to an output file
ColormapRegistry() Stores colormaps for the Image Viewer.
DataFactoryRegistry() Stores data factories.
QtClientRegistry() Stores QT widgets to visualize data.
LinkFunctionRegistry() Stores functions to convert between quantities
LinkHelperRegistry() Stores helper objects that compute many ComponentLinks at once
QtToolRegistry()
SingleSubsetLayerActionRegistry() Stores custom menu actions available when user selects a single
ProfileFitterRegistry()
DictRegistry() Base class for registries that are based on dictionaries instead of lists of objects.
glue.core.simpleforms Module

The descriptors in this module are meant to be added to classes, to specify simple user-settable forms. These classes are used to automatically construct GUIs, without having to write GUI code in the form class itself.

Option objects are defined at the class-level. To instances of these classes, an Option behaves like a normal instance attribute.

See Custom fitting plugins for example usage.

Classes
BoolOption([label, default]) A boolean-valued option
FloatOption([min, max, default, label]) An floating-point option
IntOption([min, max, default, label]) An integer-valued option
Option(default, label) Base class for other options.
glue.core.client Module
Classes
Client(data) Base class for interaction / visualization modules
BasicClient(data) Create a new client object.
glue.core.message Module
Classes
Message(sender[, tag]) Base class for messages that the hub handles.
ErrorMessage(sender[, tag]) Used to send general purpose error messages
SubsetMessage(sender[, tag]) A general message issued by a subset.
SubsetCreateMessage(sender[, tag]) A message that a subset issues when its state changes
SubsetUpdateMessage(sender[, attribute, tag]) A message that a subset issues when its state changes.
SubsetDeleteMessage(sender[, tag]) A message that a subset issues when it is deleted
DataMessage(sender[, tag]) The base class for messages that data objects issue
DataAddComponentMessage(sender, component_id)
DataUpdateMessage(sender, attribute[, tag])
DataCollectionMessage(sender[, tag])
DataCollectionActiveChange(sender[, tag])
DataCollectionActiveDataChange(sender[, tag])
DataCollectionAddMessage(sender, data[, tag])
DataCollectionDeleteMessage(sender, data[, tag])
glue.core.util Module
Functions
relim(lo, hi[, log])
split_component_view(arg) Split the input to data or subset.__getitem__ into its pieces.
join_component_view(component, view) Pack a componentID and optional view into single tuple
facet_subsets(data_collection, cid[, lo, ...]) Create a series of subsets that partition the values of
colorize_subsets(subsets, cmap[, lo, hi]) Re-color a list of subsets according to a colormap
disambiguate(label, taken) If necessary, add a suffix to label to avoid name conflicts
row_lookup(data, categories) Lookup which row in categories each data item is equal to
small_view(data, attribute) Extract a downsampled view from a dataset, for quick
small_view_array(data) Same as small_view, except using a numpy array as input
visible_limits(artists, axis) Determines the data limits for the data in a set of artists.
tick_linker(all_categories, pos, *args)
update_ticks(axes, coord, components, is_log) Changes the axes to have the proper tick formatting based on the type of component.
glue.core.visual Module
Classes
VisualAttributes([parent, washout, color]) This class is used to define visual attributes for any kind of objects
glue.utils.array Module
Functions
unique(array) Return the unique elements of the array U, as well as
shape_to_string(shape) On Windows, shape tuples use long ints which results in formatted shapes such as (2L, 3L).
view_shape(shape, view) Return the shape of a view of an array.
stack_view(shape, *views)
coerce_numeric(arr) Coerce an array into a numeric array, replacing non-numeric elements with nans.
check_sorted(array) Return True if the array is sorted, False otherwise.
glue.utils.geometry Module
Functions
points_inside_poly(x, y, vx, vy)
polygon_line_intersections(px, py[, xval, yval]) Find all the segments of intersection between a polygon and an infinite horizontal/vertical line.
glue.utils.matplotlib Module
Functions
renderless_figure()
all_artists(fig) Build a set of all Matplotlib artists in a Figure
new_artists(fig, old_artists) Find the newly-added artists in a figure
remove_artists(artists) Remove a collection of matplotlib artists from a scene
get_extent(view[, transpose])
view_cascade(data, view) Return a set of views progressively zoomed out of input at roughly constant
fast_limits(data, plo, phi) Quickly estimate percentiles in an array, using a downsampled version
defer_draw(func) Decorator that globally defers all Agg canvas draws until function exit.
color2rgb(color)
point_contour(x, y, data) Calculate the contour that passes through (x,y) in data
cache_axes(axes, toolbar) Set up caching for an axes object.
glue.utils.misc Module
Functions
nonpartial(func, *args, **kwargs) Like functools.partial, this returns a function which, when called, calls func(*args, **kwargs).
lookup_class(ref) Look up an object via its module string (e.g., ‘glue.core.Data’)
as_variable_name(x) Convert a string to a legal python variable name
as_list(x)
file_format(filename)
defer(*args, **kwds) Defer the calling of a method inside a context manager, and then call it 0 or 1 times afterwards.
Classes
DeferredMethod(method) This class stubs out a method, and provides a callable interface that logs its calls.
CallbackMixin() A mixin that provides a utility for attaching callback
PropertySetMixin An object that provides a set of properties that
Pointer(key)
glue.utils.qt Package
Functions
cmap2pixmap(cmap[, steps, size]) Convert a maplotlib colormap into a QPixmap
connect_color(client, prop, widget)
get_text([title]) Prompt the user to enter text using QT
load_ui(path[, parent, directory]) Load a .ui file
messagebox_on_error(msg) Decorator that catches exceptions and displays an error message
mpl_to_qt4_color(color[, alpha]) Convert a matplotlib color stirng into a Qt QColor object
pick_class(classes[, sort]) Prompt the user to pick from a list of classes using QT
pick_item(items, labels[, title, label, default]) Prompt the user to choose an item
process_dialog(*args, **kwds) Context manager to automatically capture the active dialog and carry out certain actions.
qt4_to_mpl_color(qcolor) Convert a QColor object into a string that matplotlib understands
set_cursor(shape) Set the Qt cursor for the duration of a function call, and unset
tint_pixmap(bm, color) Re-color a monochrome pixmap object using color
update_combobox(combo, labeldata) Redefine the items in a QComboBox
Classes
CompletionTextEdit([parent])
GlueItemWidget([parent]) A mixin for QtGui.QListWidget/GlueTreeWidget subclasses, that provides drag+drop funtionality.
GlueTabBar(*args, **kwargs)
PyMimeData([instance]) A custom MimeData object that stores live python objects
PythonListModel(items[, parent]) A Qt Model that wraps a python list, and exposes a list-like interface
QColorBox(*args, **kwargs)
QColormapCombo(*args, **kwargs)
QMessageBoxPatched(*args, **kwargs)
Worker(func, *args, **kwargs) Execute a function call on a different QThread

Indices and tables