The Legendary Legend

This could be legendary, boom, boom!

It occurs to me that legends are tables too.

Or tables are legends, if you prefer.

Both arrange text and patches, in some sort of grid.

Rows and columns, some text and a patch of colour. Or color?

The legend, has magic capabilities, including being able to inspect a plot and figure out what to use for the various labels and patches.

The patches for the legend are just handles for which there are a number of magic functions that turn the handle into an Artist to represent that handle in the legend.

The legend uses objects from matplotlib.offsetbox to do all the drawing.

These look to be the pieces that the table ought to be using.

If you think as legends as meta data regarding a plot, the row and column headings of a table, if you like, with their associated timelines, then the association with tables is stronger still.

The legend probes around in the plot data to uncover meta-data.

The legend code has some specific restrictions:

* each column is a pair (patch/text) or (text/patch).
* you can specify the number of columns
* rows calculated from the data
* some columns may be short.

The objects in matplotlib.offsetbox are more general.

notes

matplotlib.subplot_mosaic introduces an interesting ways of specifying table layouts.

After more digging around in matplotlib inards I discovered the LayoutGrid class.

_layoutgrid

This one uses constraints and a solver to deal with layouts.

There’s a lot of offsetbox code that would not be needed.

The key thing about an offsetbox.Artist that it knows the fontsize and should aim to ensure that it’s size is proportional to the fontsize. This is achieved by making things such as padding a multiple of the fontsize.

This is desirable for packing tables with text and provides a way to scale the whole image by adjusting a single fontsize variable.

It can save a lot of work over the current table, constantly measuring text.

Presumably we can add the fontsize into the whole thing as a constraint of some sort?

In short, I think I have another module to take a look at.

Update: the subplot_mosaic code has some great ideas.

It is very much focussed on axes.

Nested mosaic’s of axes open up a lot of interesting opportunities, here’s hoping we can transform these mosaics and keep track of all the axes.

subplot_mosaic gives us a figure and a dictionary of axes.

class blume.legend.Carpet[source]

A figure to manage a bunch of axes in a mosaic.

set_mosaic(mosaic, axes=None)[source]

Set the figures mosaic

Aim to do this in a way we can keep track of the axes.

Returns a dictionary of newly added axes and the (updated) existing dictionary of all axes.

class blume.legend.Cell(*args, **kwargs)[source]
set(*, agg_filter=<UNSET>, alpha=<UNSET>, animated=<UNSET>, clip_box=<UNSET>, clip_on=<UNSET>, clip_path=<UNSET>, gid=<UNSET>, height=<UNSET>, in_layout=<UNSET>, label=<UNSET>, mouseover=<UNSET>, offset=<UNSET>, path_effects=<UNSET>, picker=<UNSET>, rasterized=<UNSET>, sketch_params=<UNSET>, snap=<UNSET>, transform=<UNSET>, url=<UNSET>, visible=<UNSET>, width=<UNSET>, zorder=<UNSET>)

Set multiple properties at once.

Supported properties are

Properties:

agg_filter: a filter function, which takes a (m, n, 3) float array and a dpi value, and returns a (m, n, 3) array and two offsets from the bottom left corner of the image alpha: scalar or None animated: bool clip_box: ~matplotlib.transforms.BboxBase or None clip_on: bool clip_path: Patch or (Path, Transform) or None figure: ~matplotlib.figure.Figure gid: str height: float in_layout: bool label: object mouseover: bool offset: (float, float) or callable path_effects: list of .AbstractPathEffect picker: None or bool or float or callable rasterized: bool sketch_params: (scale: float, length: float, randomness: float) snap: bool or None transform: ~matplotlib.transforms.Transform url: str visible: bool width: float zorder: float

class blume.legend.Grid(data, inner=None, outer=None, align=None, mode=None, transpose=False, bbox=None, loc=None, prop=None)[source]

A grid of cells.

What I really need here is just create a grid of nested [HV]Packers.

But you cannot create the Packers until you have it’s children.

The way forward is less clear.

For now, just create something, so we can explore the mode

get_window_extent(renderer)[source]

Return the bounding box of the table in window coords.

set(*, agg_filter=<UNSET>, alpha=<UNSET>, animated=<UNSET>, bbox_to_anchor=<UNSET>, child=<UNSET>, clip_box=<UNSET>, clip_on=<UNSET>, clip_path=<UNSET>, gid=<UNSET>, height=<UNSET>, in_layout=<UNSET>, label=<UNSET>, mouseover=<UNSET>, offset=<UNSET>, path_effects=<UNSET>, picker=<UNSET>, rasterized=<UNSET>, sketch_params=<UNSET>, snap=<UNSET>, transform=<UNSET>, url=<UNSET>, visible=<UNSET>, width=<UNSET>, zorder=<UNSET>)

Set multiple properties at once.

Supported properties are

Properties:

agg_filter: a filter function, which takes a (m, n, 3) float array and a dpi value, and returns a (m, n, 3) array and two offsets from the bottom left corner of the image alpha: scalar or None animated: bool bbox_to_anchor: unknown child: unknown clip_box: ~matplotlib.transforms.BboxBase or None clip_on: bool clip_path: Patch or (Path, Transform) or None figure: ~matplotlib.figure.Figure gid: str height: float in_layout: bool label: object mouseover: bool offset: (float, float) or callable path_effects: list of .AbstractPathEffect picker: None or bool or float or callable rasterized: bool sketch_params: (scale: float, length: float, randomness: float) snap: bool or None transform: ~matplotlib.transforms.Transform url: str visible: bool width: float zorder: float

class blume.legend.LayoutGrid(data, inner=None, outer=None, align=None, mode=None, transpose=False, bbox=None, loc=None)[source]

A grid of cells.

What I really need here is just create a grid of nested [HV]Packers.

But you cannot create the Packers until you have it’s children.

The way forward is less clear.

For now, just create something, so we can explore the mode

class blume.legend.LegendArray(data)[source]

Draw a table from a dictionary of …

dictionaries of artists?

dictionaries of dictionaries

list of dictionaries with lists of dictionaries as values.

and so on, ad infinitum.

I think the answer will be to make this all recursive.

So hang on and lets see what goes boom!