Glue Documentation¶

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:
Recommended: Anaconda Python Distribution¶
Platforms: MacOS X, Linux, and Windows
We recommend using the Anaconda Python distribution from Continuum Analytics (or the related Miniconda distribution). Anaconda includes all of Glue’s main dependencies.
Once Anaconda or Miniconda are installed for your operating system, you can install glue using:
conda 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
Note
Anaconda installs its own version of Python
Note
There is currently a known issue when running Anaconda’s Qt on certain Linux distributions (including Kubuntu). See Issue with PyQt4 from conda for more details.
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 viapython 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:
- Python 2.7, or 3.3 and higher
- Numpy
- Matplotlib
- Pandas
- Either PyQt4 or PySide (or PyQt5, but support is still experimental)
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 topip install
all missing libraries. You can install single libraries or categories of libraries by providing additional arguments toglue-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
The main window consists of 3 areas:
- The data manager. This lists all open data sets and subsets (highlighted regions).
- The visualization area. This is where each visualization window resides.
- 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:
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:
- 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:
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.
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:
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).:
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.
Note
Make sure you switch back to the first, default combination mode (Replace Selection
mode)
Linking Data¶
Glue is designed so that visualization and drilldown can span multiple datasets. To do this, we need to inform Glue about the logical connections that exist between each dataset.
Open w5_psc.vot
– a catalog of Spitzer-identified point sources towards this region. You will see a new entry in the data manager.
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:
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:
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.
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:
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.

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”.

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:

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.
Links Propagate¶
Glue knows how to string links together. For example, consider
4 datasets which report masses in kilograms. There are
6 pairs of equivalent mass quantities (m1<->m2, m1<->m3, m1<->m4, m2<->m3, m2<->m4, m3<->m4
). However, you need only define 3 links (say, m1<->m2, m1<->m3, m1<->m4
). Even though there is no explicit link between m2<->m3
, Glue knows they are equivalent (since m3<->m1<->m2
). Glue will always be able
to figure out these “chains” of connections.
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
:

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:

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:

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:

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.
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:
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:
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 userfixed
, which is True if the parameter should be held fixedlimits
, 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.
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:
Activate this mode and click (or click+drag) a path on an image:
Hitting escape will reset the path. Hitting enter will extract this slice from the original cube, and display it in a new window:
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:
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:

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 centralDataCollection
, which is an object that holds all of the datasets, subsets, and data linkshub
is the main communication hub.application
is the top levelGlueApplication
, 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:

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:

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:

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:

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:
- You are working with multidimensional data in python, and want to use Glue for quick interactive visualization.
- 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
andy
)
qglue(uv=dict_data)
:construct a dataset labeled
uv
, with two components (u
andv
)
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
(componentsa
andb
), andastro
(componentsx
andy
)
qglue(bad=bad_data)
:doesn’t work, because the two components
x
andu
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
andcomponent_list_b
are lists of component names. In the first example above, thex
component in thexyz
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 incomponent_set_b
, given the quantities incomponent_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:
- Overplot histograms of the mass distribution of both datasets
- 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 quantityGlueApplication
takes aDataCollection
as input, and starts the GUI viastart()
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
Example Usage: Custom Link Functions¶
As an example, let’s create some translation functions which will allow us to convert temperatures in Celsius to Farenheit:
from glue.config import link_function
@link_function(info="Celsius to Fahrenheit", output_labels=['F'])
def celsius2farhenheit(c):
return c * 9. / 5. + 32
@link_function(info="Fahrenheit to Celsius", output_labels=['C'])
def farhenheit2celsius(f):
return (f - 32) * 5. / 9.
More details about this are provided in Customizing your Glue environment, but for now, let’s just assume this is how we make custom linking functions. We can copy this code into ~/.glue/config.py
file. Next time we start up Glue, the link functions now appear in the Link Dialog:

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 Link Functions¶
From the Link Data Dialog, you inform Glue how to convert between quantities among different data sets. You do this by selecting a translation function, and specifying which data attributes should be treated as inputs and outputs. You can use the configuration file to specify custom translation functions. Here’s how:
from glue.config import link_function
@link_function(info="Link from deg to rad", output_labels=['rad'])
def deg_to_rad(deg):
return deg * 3.14159 / 180
- Some remarks about this code:
link_function
is used as a decorator. The decorator adds the function to Glue’s list of link functions- We provide a short summary of the function in the
info
keyword, and a list ofoutput_labels
. Usually, only one quantity is returned, sooutput_labels
has one element. - Glue will always pass numpy arrays as inputs to a link function, and expects a numpy array (or a tuple of numpy arrays) as output
With this code in your configuration file, the deg_to_rad
function is
available in the Link Data
dialog:
This would allow you to link between two datasets with different conventions for specifying angles.
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:
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 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 thelazy_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¶

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:

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).

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 withx
andy
were provided as keywords tocustom_viewer
. They contain the data (as arrays) corresponding to the attributes labeledx
andy
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:

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:

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 integerhitrate
is set to a booleancolor
is set to'Reds'
or'Purples'
x
,y
, andhit
are passed asAttributeInfo
objects (which are just numpy arrays with a specialid
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:

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 toroi
is theglue.core.roi.Roi
object a user created – it’s only available inmake_selection
.style
is available toplot_data
andplot_subset
. It is theVisualAttributes
associated with the subset or dataset to drawstate
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
andplot_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
andplot_subset
are called whenever UI settings change. To disable this behavior, setviewer.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)

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:

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:
- Astropy for FITS images and tables, a variety of ascii table formats, and VO tables.
- scikit-image to read popular image formats like
.jpeg
and.tiff
- h5py to read HDF5 files
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.

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 aHub
.- 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.
Creating component links programmatically¶
As described in Working with Data objects, components are identified by
ComponentID
instances. We can then use these
to create links across datasets. Note that links are not defined between
Data
or Component
objects, but between ComponentID
instances.
The basic linking object is ComponentLink
.
This describes how two ComponentID
instances
are linked. The following example demonstrates how to set up a
ComponentLink
programmatically:
>>> from glue.core import Data, DataCollection
>>> d1 = Data(x1=[1, 2, 3])
>>> d2 = Data(x2=[2, 3, 4, 5])
>>> dc = DataCollection([d1, d2])
>>> from glue.core.component_link import ComponentLink
>>> link = ComponentLink([d1.id['x1']], d2.id['x2'])
Note that the first
argument of ComponentLink
should be a list of ComponentID
instances.
Since no linking function was specified in the above example,
ComponentLink
defaults to the simplest kind
of link, identity
. For the link to be useful, we need to add it to the data
collection, and we’ll be able to see what it changes:
>>> dc.add_link(link)
If we look at the list of components on the Data
objects, we see that the x2
component in d2
has been replaced by x1
:
>>> print(d1.components)
[Pixel Axis 0, World 0, x1]
>>> print(d2.components)
[Pixel Axis 0, World 0, x1]
This is because we used the identify transform, so since the
ComponentID
objects x1
and x2
are
interchangeable, Glue decided to use x1
instead of x2
in d2
for
simplicity.
The benefit of this is now that if we create a
SubsetState
based on the x1
ComponentID
, this
SubsetState
will be applicable to both datasets:
>>> subset_state = d2.id['x1'] > 2.5
>>> subset_group = dc.new_subset_group('x1 > 2.5', subset_state)
This has now created subsets in both d1
and d2
:
>>> d1.subsets[0].to_mask()
array([False, False, True], dtype=bool)
>>> d2.subsets[0].to_mask()
array([False, True, True, True], dtype=bool)
Let’s now try and use a custom linking function that is not simply identity:
>>> link = ComponentLink([d1.id['x1']], d2.id['x2'],
... using=lambda x: 2*x)
>>> dc.add_link(link)
This time, if we look at the list of components on the Data
objects, we see that d1
now has an additional component, x2
:
>>> print(d1.components)
[Pixel Axis 0, World 0, x1, x2]
>>> print(d2.components)
[Pixel Axis 0, World 0, x2]
We can take a look at the values of all the components:
>>> print(d1['x1'])
[1 2 3]
>>> print(d1['x2'])
[2 4 6]
>>> print(d2['x2'])
[2 3 4 5]
In this case, both datasets have kept their original components, but d1
now
also includes an x2
DerivedComponent
which
was computed as being twice the values of d1['x1']
.
Creating simple component links can also be done using arithmetic operations on
ComponentID
instances:
>>> d3 = Data(xa=[1, 2, 3], xb=[1, 3, 5])
>>> dc = DataCollection([d3])
>>> diff = d3.id['xa'] - d3.id['xb']
>>> diff
<BinaryComponentLink: (xa - xb)>
>>> dc.add_link(diff)
>>> d3['diff']
array([ 0, -1, -2])
Note
This is different from using comparison operators such as >
or
<=
on ComponentID
instances,
which produces SubsetState
objects.
It is also possible to add a component link to just one particular
Data
object, in which case this is equivalent to creating a DerivedComponent
. The following:
>>> from glue.core import Data
>>> d4 = Data(xa=[1, 2, 3], xb=[1, 3, 5])
>>> link = d4.id['xa'] * 2
>>> d4.add_component_link(link, 'xa_double_1')
<glue.core.component.DerivedComponent object at 0x107b2c828>
>>> print(d4['xa_double_1'])
[2 4 6]
is equivalent to creating a derived component:
>>> d4['xa_double_2'] = d4.id['xa'] * 2
>>> print(d4['xa_double_2'])
[2 4 6]
When adding a component link via the
DataCollection
add_link()
method, new
component IDs are only added to Data
objects for which
the set of ComponentID
required for the link
already exist. For instance, in the following example, xu
is only added to
d6
:
>>> d5 = Data(xs=[5, 5, 6])
>>> d6 = Data(xt=[3, 2, 3])
>>> dc = DataCollection([d5, d6])
>>> new_component = ComponentID('xu')
>>> link = ComponentLink([d6.id['xt']], new_component,
... using=lambda x: x + 3)
>>> dc.add_link(link)
>>> print(d5.components)
[Pixel Axis 0, World 0, xs]
>>> print(d6.components)
[Pixel Axis 0, World 0, xt, xu]
Built-in link functions¶
Glue includes a number of built-in link functions that are collected in the
link_function
registry object from glue.config
. You can easily create new link functions as described in Custom Link Functions, and these will then be available through the user interface, as shown in How Data Linking Works in the User guide.
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:

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.component_link Module¶
Classes¶
ComponentLink (comp_from, comp_to[, using, ...]) |
ComponentLinks represent transformation logic between ComponentIDs |
BinaryComponentLink (left, right, op) |
A ComponentLink that combines two inputs with a binary function |
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) |
|
||||
MplYRangeROI (axes) |
|
||||
XRangeROI ([min, max]) |
|||||
RangeROI (orientation[, min, max]) |
|
||||
YRangeROI ([min, max]) |
|||||
VertexROIBase ([vx, vy]) |
|
||||
CategoricalROI ([categories]) |
A ROI abstraction to represent selections of categorical data. |
glue.core.link_helpers Module¶
This module provides several classes and LinkCollection classes to assist in linking data.
The functions in this class (and stored in the __LINK_FUNCTIONS__
list) define common coordinate transformations. They are meant to be
used for the using parameter in
glue.core.component_link.ComponentLink
instances.
The LinkCollection
class and its sublcasses are factories to create
multiple ComponentLinks easily. They are meant to be passed to
add_link()
Classes¶
LinkCollection |
|
LinkSame (cid1, cid2) |
Return ComponentLinks to represent that two componentIDs |
LinkTwoWay (cid1, cid2, forwards, backwards) |
Return 2 links that connect input ComponentIDs in both directions |
MultiLink (cids_left, cids_right[, forwards, ...]) |
Compute all the ComponentLinks to link groups of ComponentIDs |
LinkAligned (data) |
Compute all the links to specify that the input data are pixel-aligned. |
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
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 |