No project description provided
Project description
pyviewarr
🤖 Clanker code disclaimer: written (largely) with large language model coding tools. Use at your own risk. 🤖
A faster, more intuitive way to explore 2D data (i.e. monochromatic images) in Python notebooks
Installation
pip install pyviewarr
or with uv:
uv add pyviewarr
Usage
First, open a notebook interface like JupyterLab (or another supporting anywidget).
Basic
pyviewarr.show() returns a widget instance. If it's the last line of your cell, it will display automatically.
import numpy as np
x = np.arange(16).reshape(4, 4)
pyviewarr.show(x)
Width and height of the widget can be set in the call to show():
pyviewarr.show(x, width=500, height=400)
You can also explicitly display with IPython.display.display():
widget = pyviewarr.show(x)
from IPython.display import display
display(widget)
If there are more than two axes, the last two axes are treated as Y and X (NumPy convention) and navigation buttons are shown for any others.
Navigating in a large datacube remains responsive because only one slice/image is sent to the viewer at a time.
Image stretching
Mapping array values to images for display involves normalization (stretching) which can include nonlinear transformations.
For all of these manipulations, you can reset them with the reset button:
Stretching
Right-clicking and dragging on the image viewer widget changes the contrast and bias. (If you have used ds9, this will feel familiar.)
Limits
When the image loads, the viewer sets the color limits to min(image) and max(image). Type in new limits if you want.
If you want to avoid resetting the limits (e.g. when paging through a data cube) use the lock button to fix the limits at their current values.
Linear/Log
Only three colormaps are included: gray, inferno, and magma (from matplotlib). Log stretch behaves like ds9 as well, such that negative values are rescaled and don't make invalid pixel values.
Diverging (±)
When examining residuals, you may want a diverging colormap centered at zero. Use the ± button to switch the limits to symmetric and the available colormaps to RdBu and RdYlBu (from matplotlib).
Zooming, panning, rotation
Zoom by clicking the +/- buttons at lower right, scrolling with your mouse/trackpad, or using the - and = keys. Reset zoom with the "reset zoom to fit" button or the 0 key.
Pan by clicking and dragging. Cmd/Ctrl-Click to center the pixel under the cursor.
Use the left and right arrow buttons to rotate in 15º increments, or enter a number in degrees (counter-clockwise) in the box between them.
By default, rotation is about the image center, but you can set a pivot point by Cmd/Ctrl-Shift-clicking the desired point.
Matplotlib integration
Once you've rotated and panned and stretched and zoomed you may want to save a plot, but the viewer doesn't handle plotting.
Fortunately, the widget has a plot_to_matplotlib() method that takes a matplotlib.axes.Axes instance:
fig, ax = plt.subplots()
widget.plot_to_matplotlib(ax)
Note: Rotating the axes makes the matplotlib tick labels useless, so they are hidden.
If you just want the contrast / bias / stretch, you can get a norm from the get_normalization() method:
norm = widget.get_normalization()
import matplotlib.pyplot as plt
plt.imshow(x, norm=norm)
Widget API
ViewerConfig
Using the ViewerConfig dataclass or passing its arguments to show() lets you set the initial configured state of the viewer.
pyviewarr.show(x, vmin=-10, vmax=10)
See example.
Setting data
If you retain a reference to the widget, you can set its contents from a later cell (if you want).
widget = pyviewarr.show(x)
widget
Now you can change the live widget's contents by supplying a new array to set_array().
new_arr = np.random.randn(512, 512).astype(np.float32) * 100
widget.set_array(new_arr)
Development
Be sure to clone with git clone --recurse-submodules (or, if you cloned first and then read this, git submodule update --init --recursive to initialize an existing clone).
The frontend part of the widget (i.e. viewarr itself) is written in Rust with egui, requiring a Rust compiler toolchain for wasm32-unknown-unknown and the wasm-pack tool installed.
Install rust with rustup: https://rustup.rs/
Now that you have cargo, install wasm-pack with cargo install wasm-pack.
Using uv for development will automatically manage virtual environments and dependencies for you.
For live reloading in development:
export ANYWIDGET_HMR=1
Export this variable before starting JupyterLab so it will use hot module reloading.
uv run jupyter lab example.ipynb
Alternatively, create and manage your own virtual environment:
python -m venv .venv
source .venv/bin/activate
pip install -e ".[dev]"
The widget front-end code bundles it's JavaScript dependencies. After setting up Python, make sure to install these dependencies locally:
npm install
While developing, you can run the following in a separate terminal to automatically rebuild JavaScript as you make changes:
npm run dev
Open example.ipynb in JupyterLab, VS Code, or your favorite editor
to start developing. Changes made in js/ will be reflected
in the notebook.
To do a full build, use the project script:
npm run build:all
End-to-end tests (Galata + Playwright)
The widget has browser E2E tests using JupyterLab's Galata helpers.
First-time setup for browser binaries:
npm run test:e2e:install
Run E2E tests headless:
npm run test:e2e
Run E2E tests with a visible browser:
npm run test:e2e:headed
The test server uses tests/e2e/jupyter_server_config.py, which enables Galata's JupyterLab test hooks.
Releasing
Note that viewarr/ submodule changes will need to be committed and pushed first, then the submodule reference in this repository can be updated.
GitHub releases are automatically pushed to PyPI by the workflow in .github/workflows/ci.yml.
Changelog
Unreleased (since v0.3.0)
pyviewarr changes
- Added browser end-to-end testing with Playwright + JupyterLab Galata (
playwright.config.ts,tests/e2e/). - Added CI coverage for Playwright E2E tests in
.github/workflows/ci.yml. - Expanded development documentation for build/test workflows.
- Updated bundled
viewarrsubmodule from3d74f69to3797ee7.
Included viewarr changes
- Fixed Cmd/Ctrl-click centering coordinate mapping.
- Pinned wasm-bindgen stack to avoid
glow/js-sysCI build breakages. - Added/expanded viewer state APIs in JS/TS:
setViewerState(...)for partial bulk state updates.getZoom(...)/setZoom(...).setColormap(...)/setColormapReversed(...).- New
StretchModeandViewerStateConfigtypings.
- Improved colormap name handling/parsing with canonical names and aliases (
gray/grayscale/greyscale, etc.).
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file pyviewarr-0.3.1.tar.gz.
File metadata
- Download URL: pyviewarr-0.3.1.tar.gz
- Upload date:
- Size: 2.0 MB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
621ec98e8ed5a8dbc03d0ae7cbbe53f96d1a29228c61ed68cac1439b24131789
|
|
| MD5 |
8136216ae351700087ecbf90c687075e
|
|
| BLAKE2b-256 |
40f7308dbd05a092bd13969ebb6588c86ac9a8d61cdc969f1d5ef5c390973786
|
Provenance
The following attestation bundles were made for pyviewarr-0.3.1.tar.gz:
Publisher:
ci.yml on joseph-long/pyviewarr
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
pyviewarr-0.3.1.tar.gz -
Subject digest:
621ec98e8ed5a8dbc03d0ae7cbbe53f96d1a29228c61ed68cac1439b24131789 - Sigstore transparency entry: 976495488
- Sigstore integration time:
-
Permalink:
joseph-long/pyviewarr@55d64ed1987f87d949f57dd4ec6bc4ba8bd34c5a -
Branch / Tag:
refs/tags/v0.3.1 - Owner: https://github.com/joseph-long
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
ci.yml@55d64ed1987f87d949f57dd4ec6bc4ba8bd34c5a -
Trigger Event:
push
-
Statement type:
File details
Details for the file pyviewarr-0.3.1-py3-none-any.whl.
File metadata
- Download URL: pyviewarr-0.3.1-py3-none-any.whl
- Upload date:
- Size: 2.0 MB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
de70d9293c8bf777c2acde3083388d780a54a6ca0f3f8cee7a4c42592e0d54ea
|
|
| MD5 |
3ed6bfc6be42bbc3f50abd93a68c612f
|
|
| BLAKE2b-256 |
def066dfafd569272e54d6b7204f58baa054d8cad82c43e25f0b572c14995b5c
|
Provenance
The following attestation bundles were made for pyviewarr-0.3.1-py3-none-any.whl:
Publisher:
ci.yml on joseph-long/pyviewarr
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
pyviewarr-0.3.1-py3-none-any.whl -
Subject digest:
de70d9293c8bf777c2acde3083388d780a54a6ca0f3f8cee7a4c42592e0d54ea - Sigstore transparency entry: 976495490
- Sigstore integration time:
-
Permalink:
joseph-long/pyviewarr@55d64ed1987f87d949f57dd4ec6bc4ba8bd34c5a -
Branch / Tag:
refs/tags/v0.3.1 - Owner: https://github.com/joseph-long
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
ci.yml@55d64ed1987f87d949f57dd4ec6bc4ba8bd34c5a -
Trigger Event:
push
-
Statement type: