https://github.com/hyperspy/hyperspy_gui_anywidget
anywidget GUI elements for the HyperSpy framework
https://github.com/hyperspy/hyperspy_gui_anywidget
Last synced: about 1 month ago
JSON representation
anywidget GUI elements for the HyperSpy framework
- Host: GitHub
- URL: https://github.com/hyperspy/hyperspy_gui_anywidget
- Owner: hyperspy
- License: other
- Created: 2026-05-25T09:37:11.000Z (about 2 months ago)
- Default Branch: master
- Last Pushed: 2026-06-08T18:34:29.000Z (about 1 month ago)
- Last Synced: 2026-06-08T20:17:03.513Z (about 1 month ago)
- Language: Python
- Homepage: https://hyperspy-gui-anywidget.readthedocs.io/
- Size: 186 KB
- Stars: 0
- Watchers: 0
- Forks: 2
- Open Issues: 7
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.rst
- Contributing: CONTRIBUTING.md
- License: LICENSE
- Code of conduct: CODE_OF_CONDUCT.md
- Agents: .github/AGENTS.md
Awesome Lists containing this project
README
# hyperspy_gui_anywidget
anywidget GUI elements for the HyperSpy framework.
This package provides GUI widgets using [anywidget](https://anywidget.dev/) for the [HyperSpy](https://hyperspy.org/) scientific data analysis library. It is explicitly supported in Jupyter Notebook / JupyterLab and in [Marimo](https://marimo.io/) without any frontend build step or bundler.
## Installation
### With pip
```bash
pip install hyperspy_gui_anywidget
```
### With optional test dependencies
```bash
pip install "hyperspy_gui_anywidget[tests]"
```
### Development install
```bash
git clone https://github.com/hyperspy/hyperspy_gui_anywidget.git
cd hyperspy_gui_anywidget
pip install -e ".[dev,tests,doc]"
```
## Usage
### Jupyter Notebook / JupyterLab
For modern Jupyter environments, installing `hyperspy_gui_anywidget` is enough. You do not
need to manually enable an anywidget extension in current JupyterLab or Notebook releases,
because `anywidget`/`ipywidgets` handle the widget integration.
In normal use, the default `display=True` behavior is what you want: calling a GUI method
displays the widget inline immediately.
HyperSpy can then use the anywidget toolkit directly:
```python
import hyperspy.api as hs
# Open preferences GUI
hs.preferences.gui(toolkit="anywidget")
```
Widgets are displayed inline using `IPython.display.display()`:
```python
roi = hs.roi.SpanROI(left=0, right=10)
roi.gui(toolkit="anywidget")
```
### Marimo
Marimo follows the same default behavior: with `display=True` (the default), the widget is
displayed inline for you.
If you need the raw widget object for manual embedding with `mo.ui.anywidget()`, pass
`display=False` and use the returned dictionary instead:
```python
import marimo as mo
import hyperspy.api as hs
roi = hs.roi.SpanROI(left=0, right=10)
res = roi.gui(toolkit="anywidget", display=False)
widget = res["anywidget"]["widget"]
mo.ui.anywidget(widget)
```
The `add_display_arg` decorator detects Marimo at runtime (via `sys.modules`) and, when
needed, flattens `VBox`/`HBox` containers before displaying them. Use `display=False` only
when you need the raw `{"widget": ..., "wdict": ...}` dictionary for embedding,
inspection, or tests.
### Supported environments
This project is explicitly tested and documented for:
- Jupyter Notebook / JupyterLab
- Marimo
Because the leaf widgets are implemented with `anywidget`, they may also work in other
frontends that support Jupyter widgets / anywidget. However, this repository does not
explicitly test or guarantee behavior in environments such as VS Code notebooks, Google
Colab, JupyterLite, or Voila.
## Architecture
This package is built on a few key design decisions:
1. **AnyWidget subclasses with inline `_esm` JavaScript** — Every widget is a Python class extending `anywidget.AnyWidget` with an inline JavaScript string. There is no npm, webpack, or build step. The JS renders standard HTML elements (``, ``, ``) and syncs state via traitlets.
2. **`link_traits` for bidirectional sync** — Enthought Traits (used by HyperSpy) and traitlets (used by anywidget) are bridged with `link_traits.link()`. Changes in the GUI propagate to the HyperSpy object and vice versa.
3. **ContainerWidget bridges Jupyter and Marimo** — On the Jupyter path, `ContainerWidget` deliberately uses real `ipywidgets` containers such as `VBox`, `HBox`, `Tab`, and `Accordion` so the GUI integrates with the native widget stack. On the Marimo path, those layouts are flattened into `FlatContainer` configs rendered by anywidget.
4. **Environment-aware display** — The `@add_display_arg` decorator handles the difference between Jupyter and Marimo. In Marimo it flattens `VBox`/`HBox` roots before display when needed, and `display=False` always returns the raw `{"widget": ..., "wdict": ...}` dictionary.
5. **33 widget functions** — All GUI functions return a dictionary with `"widget"` (the root AnyWidget) and `"wdict"` (a mapping of named sub-widgets). This pattern is consistent across ROI widgets, axis widgets, model widgets, tool widgets, and preference widgets.
## Running the tests
pytest is required to run the tests.
```bash
pip install "hyperspy_gui_anywidget[tests]"
pytest --pyargs hyperspy_gui_anywidget
```
## Examples
See the `examples/` directory:
- `examples/comparison_notebook.ipynb` — clean `ipywidgets` vs `anywidget` side-by-side comparison for representative HyperSpy GUIs ([GitHub](https://github.com/hyperspy/hyperspy_gui_anywidget/blob/main/examples/comparison_notebook.ipynb), [Binder](https://mybinder.org/v2/gh/hyperspy/hyperspy_gui_anywidget/HEAD?filepath=examples%2Fcomparison_notebook.ipynb))
- `examples/jupyter_anywidget_example.ipynb` — practical Jupyter Notebook / JupyterLab walkthrough using the default `display=True` flow ([GitHub](https://github.com/hyperspy/hyperspy_gui_anywidget/blob/main/examples/jupyter_anywidget_example.ipynb), [Binder](https://mybinder.org/v2/gh/hyperspy/hyperspy_gui_anywidget/HEAD?filepath=examples%2Fjupyter_anywidget_example.ipynb))
- `examples/marimo_anywidget_example.py` — equivalent Marimo app showing explicit `mo.ui.anywidget(...)` embedding via `display=False` ([GitHub](https://github.com/hyperspy/hyperspy_gui_anywidget/blob/main/examples/marimo_anywidget_example.py))
The Jupyter notebook examples link to Binder so they open in a live notebook session instead of a
static renderer. The Marimo example is currently linked as source only: this repository does not
yet publish a verified dynamic Marimo deployment for that example.
## Development
Contributions through pull requests are welcome. Start with [CONTRIBUTING.md](CONTRIBUTING.md) for this repository's setup, testing, docs, and changelog workflow, and see the [HyperSpy Developer Guide](http://hyperspy.org/hyperspy-doc/current/dev_guide.html) for the broader project conventions.
When adding a new widget:
1. Create an AnyWidget subclass (or reuse one from `custom_widgets.py`)
2. Use `link_traits.link()` to sync with HyperSpy traits
3. Wrap everything in a `ContainerWidget`
4. Apply `@add_display_arg` for environment-aware display
5. Register the widget in `hyperspy_extension.yaml`
6. Add tests in `hyperspy_gui_anywidget/tests/`
`ipywidgets` is therefore an explicit runtime dependency by design: anywidget provides the custom leaf widgets, while `ipywidgets` still provides the Jupyter-native container layer that this package bridges to Marimo.
Before opening a pull request, run:
```bash
pre-commit run --all-files
pytest --pyargs hyperspy_gui_anywidget
sphinx-build -W -b html docs docs/_build/html
```
User-facing changes should also include a short news fragment in `upcoming_changes/`.
## License
This project is licensed under the GNU General Public License v3 (GPLv3).