Metadata-Version: 2.5
Name: seeable
Version: 0.1.0
Summary: Live visual explorer for multidimensional Python data.
Project-URL: Homepage, https://github.com/tom-draper/seeable
Project-URL: Repository, https://github.com/tom-draper/seeable
Project-URL: Issues, https://github.com/tom-draper/seeable/issues
Project-URL: Changelog, https://github.com/tom-draper/seeable/blob/main/CHANGELOG.md
Author: Seeable contributors
License: MIT License
        
        Copyright (c) 2026 Tom Draper
        
        Permission is hereby granted, free of charge, to any person obtaining a copy
        of this software and associated documentation files (the "Software"), to deal
        in the Software without restriction, including without limitation the rights
        to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
        copies of the Software, and to permit persons to whom the Software is
        furnished to do so, subject to the following conditions:
        
        The above copyright notice and this permission notice shall be included in all
        copies or substantial portions of the Software.
        
        THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
        IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
        FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
        AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
        LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
        OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
        SOFTWARE.
License-File: LICENSE
Keywords: arrays,microscopy,qt,visualisation,xarray
Classifier: Development Status :: 3 - Alpha
Classifier: Environment :: X11 Applications :: Qt
Classifier: Intended Audience :: Science/Research
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Scientific/Engineering :: Visualization
Requires-Python: >=3.10
Requires-Dist: numpy>=1.23
Requires-Dist: pyside6>=6.6
Provides-Extra: all
Requires-Dist: anndata>=0.10; extra == 'all'
Requires-Dist: dask[array]>=2024.1; extra == 'all'
Requires-Dist: h5py>=3.7; extra == 'all'
Requires-Dist: ipywidgets>=8; extra == 'all'
Requires-Dist: jax>=0.4; extra == 'all'
Requires-Dist: pandas>=2; extra == 'all'
Requires-Dist: pillow>=10; extra == 'all'
Requires-Dist: polars>=1; extra == 'all'
Requires-Dist: pyarrow>=14; extra == 'all'
Requires-Dist: scipy>=1.10; extra == 'all'
Requires-Dist: torch>=2.0; extra == 'all'
Requires-Dist: xarray>=2023.1; extra == 'all'
Requires-Dist: zarr>=2.16; extra == 'all'
Provides-Extra: anndata
Requires-Dist: anndata>=0.10; extra == 'anndata'
Provides-Extra: arrow
Requires-Dist: pyarrow>=14; extra == 'arrow'
Provides-Extra: cupy
Requires-Dist: cupy>=12; extra == 'cupy'
Provides-Extra: dask
Requires-Dist: dask[array]>=2024.1; extra == 'dask'
Provides-Extra: dev
Requires-Dist: anndata>=0.10; extra == 'dev'
Requires-Dist: dask[array]>=2024.1; extra == 'dev'
Requires-Dist: h5py>=3.7; extra == 'dev'
Requires-Dist: hatchling>=1.20; extra == 'dev'
Requires-Dist: ipywidgets>=8; extra == 'dev'
Requires-Dist: pandas>=2; extra == 'dev'
Requires-Dist: pillow>=10; extra == 'dev'
Requires-Dist: polars>=1; extra == 'dev'
Requires-Dist: pyarrow>=14; extra == 'dev'
Requires-Dist: pytest>=7; extra == 'dev'
Requires-Dist: scipy>=1.10; extra == 'dev'
Requires-Dist: xarray>=2023.1; extra == 'dev'
Requires-Dist: zarr>=2.16; extra == 'dev'
Provides-Extra: hdf5
Requires-Dist: h5py>=3.7; extra == 'hdf5'
Provides-Extra: images
Requires-Dist: pillow>=10; extra == 'images'
Provides-Extra: jax
Requires-Dist: jax>=0.4; extra == 'jax'
Provides-Extra: jupyter
Requires-Dist: ipywidgets>=8; extra == 'jupyter'
Provides-Extra: pandas
Requires-Dist: pandas>=2; extra == 'pandas'
Provides-Extra: polars
Requires-Dist: polars>=1; extra == 'polars'
Provides-Extra: sparse
Requires-Dist: scipy>=1.10; extra == 'sparse'
Provides-Extra: torch
Requires-Dist: torch>=2.0; extra == 'torch'
Provides-Extra: xarray
Requires-Dist: xarray>=2023.1; extra == 'xarray'
Provides-Extra: zarr
Requires-Dist: zarr>=2.16; extra == 'zarr'
Description-Content-Type: text/markdown

# Seeable

Seeable is a live visual explorer for multidimensional Python data. Pass an
array to `seeable.view()` and start exploring, without choosing a plot first.

```python
import numpy as np
import seeable

data = np.random.random((50, 128, 128))
viewer = seeable.view(data)

data[20:30, 40:90, 40:90] *= 5
viewer.refresh()
viewer.snapshot("seeable.png")                       # the visualisation
viewer.snapshot("window.png", canvas_only=False)     # sidebar included

# Or refresh a desktop viewer automatically every 500 ms.
live_viewer = seeable.view(data, live=True)

# Reuse the same visual configuration with compatible data.
saved_view = viewer.export_state()
another_viewer = seeable.view(data.copy())
another_viewer.apply_state(saved_view)

# Or persist that configuration for a future session.
viewer.save_state("seeable-view.json")
another_viewer.load_state("seeable-view.json")
```

Lists, tuples, ranges, buffers and anything else that already holds numbers are
accepted directly. NumPy arrays need no extra. Pandas and Polars DataFrames, Xarray DataArrays and
Datasets, chunked Dask and Zarr arrays, PyTorch tensors, and JAX arrays come
with the extra of the same name; SciPy sparse matrices come with `sparse`, and
HDF5 files with `hdf5` -- a whole file is browsable as the tree of datasets it
holds, each read only where it is looked at. Arrow tables come with `arrow`,
Pillow images with `images`, AnnData with `anndata`, and CuPy arrays with `cupy`. Dictionaries of any supported
value are browsable as a tree, and lists of dictionaries or column-oriented
dictionaries are recognised as record tables. Anything else can be made
viewable — see [Custom sources](#custom-sources).

The native Qt interface draws its own heatmaps, with pan and cursor-centred
zoom, dimension remapping, slice controls, and value probes.

## Install

```bash
pip install -e '.[all]'
```

The first renderer is a desktop Qt application. In a Jupyter kernel,
`seeable.view(data)` returns the same viewer and displays an inline widget when
it is the cell's final value. Install the `jupyter` extra for this mode. The
inline surface shares viewer state and refresh behaviour with Qt; a WebGL canvas
will eventually replace its lightweight Python-rendered preview.
It includes X/Y/Z role controls and sliders for the remaining dimensions.

## Custom sources

Anything can be made viewable by presenting it as a `Seeable`. Only `shape`,
`dtype`, `dimensions`, `name`, `metadata`, and `get_slice` are required; the
rest of the interface describes optional abilities and defaults to "this source
does not have that".

```python
import seeable

class MeshField(seeable.Seeable):
    def __init__(self, mesh):
        self.mesh = mesh

    @property
    def shape(self):
        return self.mesh.grid.shape

    @property
    def dtype(self):
        return self.mesh.grid.dtype

    @property
    def dimensions(self):
        return ("depth", "row", "column")

    @property
    def name(self):
        return self.mesh.label

    @property
    def metadata(self):
        return {"source": "MeshField", "attrs": {"units": self.mesh.units}}

    def get_slice(self, selection):
        return self.mesh.grid[selection]        # only the visible region

seeable.view(MeshField(mesh))
```

A `Seeable` presents a source rather than copying it: `get_slice` is asked only
for the region on screen, so a lazy, chunked, or remote source stays that way.

To have `seeable.view()` accept your object directly, either give it a
`__seeable__()` method returning a `Seeable`, or register a rule for it:

```python
class Mesh:
    def __seeable__(self):
        return MeshField(self)

seeable.view(mesh)

# ...or, for a type you do not own:
seeable.register(lambda data: isinstance(data, Mesh), MeshField)
```

Registered rules are consulted before the built-in ones, so they can take over a
type Seeable already recognises.

A source made of named parts — a dictionary, an Xarray Dataset, a group of
grids — reports them as `children`, and nested parts become dotted paths:

```python
class Survey(seeable.Seeable):
    @property
    def children(self):
        return {"north": MeshField(self.north), "south": MeshField(self.south)}

seeable.view(survey).select_variable("north")
```

## Keyboard

| Key | Action |
| --- | --- |
| `←` `→` | Step the active slider |
| `Space` | Play or pause |
| `A` | Auto range |
| `R` | Reset pan, zoom and probe |
| `Ctrl+S` / `Ctrl+O` | Save or load the view state |
| `Ctrl+Shift+S` | Save an image |
| `?` | List the shortcuts |

## Current scope

* Custom Qt heatmaps and 1D line views with pan, cursor-centred zoom, and double-click reset; 3D uses linked slice views, plus an orbitable WebGL point cloud where QtWebEngine is installed.
* The opening view is chosen from what the source is, not only from how many axes it has.
* Bar charts for named values: `seeable.view({"apples": 3, "pears": 7})`.
* Scatter plots for rows of coordinates: an `(N, 2)` array is points, and a third column colours them.
* Line charts for numeric tables, drawn against the index -- a dated DataFrame opens as a time series.
* Labelled axes on every heatmap, reading the source's own coordinates.
* Clickable value probes for inspecting coordinates and values in visible image and volume slices.
* Horizontal and vertical profiles through any probed heatmap cell.
* Semantic Xarray dimension names and metadata.
* Composite sources — dictionaries, Xarray Datasets — browsable by part, with nested leaves such as `sensors.temperature`, inline scalar/list inspection, and a bounded structure summary.
* Searchable, sortable record tables with field visibility controls, full-row inspection, and field-driven histograms and scatter plots.
* Native Pandas and Polars DataFrame exploration with the same table and numeric-field tools.
* Dask arrays, on their own or behind Xarray, stay lazy until their visible region is rendered.
* A source that is slow to read is fetched in the background, so scrubbing never freezes the window, and a source that fails says so in the status bar.
* Coordinate-aware time scrubbing and play/pause for Xarray time dimensions.
* Manual updates through `viewer.refresh()` and optional periodic desktop refresh with `live=True`.
* Inline notebook previews and slice controls with the `jupyter` extra.
* Colour pictures shown as pictures: an eight-bit array ending in three or four channels is drawn in its own colours rather than as one plane per channel.
* Contrast clipped to a percentile pair by default, a gamma control, and maximum/attenuated/mean/minimum projections through a volume, where attenuation dims what stands behind something.
* Fourteen palettes drawn in the selector: the perceptually uniform viridis, plasma, inferno, magma and cividis, a diverging map built in CIELAB for signed data, plus gray, spectrum, and single-hue ramps for channel overlays.
* Exportable dimension mappings, slice selections, and colour settings.
* A public `Seeable` interface, a `__seeable__()` protocol, and `seeable.register()` for custom sources.

True GPU volume rendering is the next renderer: the cube view currently draws a
density-filtered point cloud through WebGL, and always in viridis regardless of
the selected palette.
