Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 0 additions & 6 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,6 @@ repos:
- id: trailing-whitespace
exclude: ^\.napari-hub/.*
- id: check-yaml # checks for correct yaml syntax for github actions ex.
exclude:
(?x)(^src/ndevio/ndev_settings\.yaml$)
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.14.8
hooks:
Expand All @@ -18,7 +16,3 @@ repos:
rev: v0.3.0
hooks:
- id: napari-plugin-checks
- repo: https://github.com/ndev-kit/ndev-settings
rev: v0.4.0
hooks:
- id: reset-settings-values
4 changes: 2 additions & 2 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ The napari reader plugin ([_napari_reader.py](src/ndevio/_napari_reader.py)) imp

- **Plugin discovery**: Registers file extensions and provides reader selection
- **Scene handling**: Integrates with the Scene Widget for multi-scene files
- **Settings integration**: Respects user preferences from ndev-settings
- **Settings integration**: Respects user preferences from napari's plugin settings (`contributions.configurations` in `napari.yaml`)
- **Error handling**: Provides helpful suggestions for missing bioio plugins

This module is the **bridge between napari and nImage**, translating drag-and-drop file operations into proper layer creation.
Expand All @@ -71,7 +71,7 @@ This module is the **bridge between napari and nImage**, translating drag-and-dr

1. **Core logic separate from UI**: Business logic in `nimage.py`, `_layer_utils.py`, etc. should have **no Qt or napari imports** (except type hints)
2. **Lazy imports**: Expensive imports (Qt, napari widgets) should be lazy-loaded to keep startup fast
3. **Settings-driven behavior**: Use [ndev-settings] for user-configurable behavior rather than hardcoding choices
3. **Settings-driven behavior**: Use napari plugin settings (declared in `napari.yaml` under `contributions.configurations`) for user-configurable behavior rather than hardcoding choices
4. **bioio compatibility**: Stay aligned with bioio's data model and avoid reinventing functionality

## Testing
Expand Down
25 changes: 14 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ Originally developed as part of napari-ndev (as a spiritual successor to [napari
- **Thorough metadata extraction** — extract and apply scale, units, axis labels, metadata (inc. OME) to napari layers
- **Remote file support** — compatible Bioio readers,such as [bioio-ome-zarr], can read from remote filesystems (HTTP, S3, etc.) with dask-backed loading
- **Native multiscale support** — automatically read and display multiscale images when supported by the reader. For best experience, turn on the asynchronous rendering experimental setting in napari.
- **Configurable behavior** via [ndev-settings] — customize reader priority, multi-scene handling, and more
- **Configurable behavior** via napari Preferences — customize multi-scene handling, memory policy, plugin suggestions, and more
- **Smart plugin installation** — automatic suggestions to install missing bioio reader plugins
- **Programmatic API** — `nImage` class for napari-ready metadata extraction
- **Batch utilities** — legacy widget for batch concatenation (with [nbatch]) and metadata management, with features being superseded by [napari-metadata]
Expand Down Expand Up @@ -87,17 +87,18 @@ If you open a file that requires a bioio reader not currently installed, ndevio

This widget taps into the `napari-plugin-manager` to install the bioio reader plugin from PyPI via a GUI. You may invoke this widget manually at any time via `Plugins > ndevio > Install BioIO Reader Plugins` to install any additional bioio reader plugin *and* update any currently installed plugins.

#### Settings Widget
#### Settings

Access **ndevio settings** via `Plugins > ndev-settings > Settings` to customize:
Configure **ndevio settings** in napari's **Preferences** dialog (under the
Plugins section, select `ndevio`) to customize:

- **Preferred reader**: Override bioio's default plugin selection priority (useful for formats with multiple compatible readers)
- **Multi-scene handling**: Choose whether to show the scene widget, view all scenes as a stack, or view only the first scene
- **Multi-scene handling**: Choose whether to show the scene widget, view all scenes as layers, or view only the first scene
- **Plugin suggestions**: Enable/disable automatic plugin installation prompts for unsupported formats
- **Memory policy**: Set the maximum uncompressed image size (GB) to load eagerly; larger images use dask
- **Export**: Set the canvas scale and (optionally) override the screenshot canvas size

![ndevio settings via the ndev-settings widget in napari](https://github.com/ndev-kit/ndevio/blob/main/resources/ndev-settings.png?raw=true)

These settings are managed by [ndev-settings] and persist across napari sessions.
These settings are declared in ndevio's plugin manifest (`contributions.configurations`),
managed by napari, and persist across napari sessions.

#### Utilities Widget

Expand Down Expand Up @@ -165,10 +166,13 @@ Image metadata is extracted from bioio and converted to napari layer metadata ba

Images are loaded **in-memory** or **lazily** (via dask) automatically based on:

- File size < 4 GB **AND**
- Uncompressed file size below the configured in-memory limit (default 8 GB) **AND**
- File size < 30% of available RAM
- Remote files (e.g., S3, HTTP) and multiscale are always loaded lazily

The in-memory limit is configurable via the `ndevio` plugin settings in napari's
Preferences dialog.

### Multi-channel Images

Multi-channel images are **always split** into individual layers (one per channel), using channel names from metadata when available. Images are added with colorblind-friendly colormaps.
Expand Down Expand Up @@ -216,9 +220,8 @@ If you encounter any problems, please [file an issue] along with a detailed desc
[tox]: https://tox.readthedocs.io/en/latest/
[bioio]: https://github.com/bioio-devs/bioio
[napari-aicsimageio]: https://github.com/AllenCellModeling/napari-aicsimageio
[ndev-settings]: https://github.com/ndev-kit/ndev-settings
[napari-metadata]: https://github.com/napari/napari-metadata
[nbatch]: https://github.com/ndev-kit/nbatch
[napari-metadata]: https://github.com/napari/napari-metadata
[uv]: https://docs.astral.sh/uv/
[ndev-kit]: https://github.com/ndev-kit
[bioio-ome-zarr]: https://github.com/bioio-devs/bioio-ome-zarr
8 changes: 2 additions & 6 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ requires-python = ">=3.11"
# See best practices: https://napari.org/stable/plugins/building_a_plugin/best_practices.html
dependencies = [
"napari",
"ndev-settings>=0.4.2",
"nbatch>=0.0.4",
"natsort",
"magicgui",
Expand All @@ -56,17 +55,14 @@ dev = [
"pytest", # https://docs.pytest.org/en/latest/contents.html
"pytest-cov", # https://pytest-cov.readthedocs.io/en/latest/
"pytest-qt", # https://pytest-qt.readthedocs.io/en/latest/
"napari",
"pyqt6", # Explicitly use PyQt6 (napari's future default, has ARM64 macOS support)
"napari[all]>=0.9.0rc0", # for testing plugin settings
"npe2>=0.9.0rc0", # for testing plugin settings
"bioio-czi", # Include an additional priority reader for development/testing
]

[project.entry-points."napari.manifest"]
ndevio = "ndevio:napari.yaml"

[project.entry-points."ndev_settings.manifest"]
ndevio = "ndevio:ndev_settings.yaml"

[project.urls]
"Bug Tracker" = "https://github.com/ndev-kit/ndevio/issues"
"Documentation" = "https://github.com/ndev-kit/ndevio#README.md"
Expand Down
12 changes: 6 additions & 6 deletions src/ndevio/_napari_reader.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,20 +38,20 @@ def napari_get_reader(
The reader function for the given path
"""

from ndev_settings import get_settings
from ._settings import get_ndevio_settings

settings = get_settings()
settings = get_ndevio_settings()

open_first_scene_only = (
open_first_scene_only
if open_first_scene_only is not None
else settings.ndevio_reader.scene_handling == 'View First Scene Only' # type: ignore
else settings.reader.scene_handling == 'View First Scene Only'
) or False

open_all_scenes = (
open_all_scenes
if open_all_scenes is not None
else settings.ndevio_reader.scene_handling == 'View All Scenes' # type: ignore
else settings.reader.scene_handling == 'View All Scenes'
) or False

# Return reader function; actual format validation happens in
Expand All @@ -71,8 +71,8 @@ def napari_reader_function(
"""
Read a file using bioio.

nImage handles reader selection: if a preferred_reader is set in settings,
it's tried first with automatic fallback to bioio's default plugin ordering.
nImage handles reader selection, relying on bioio's default plugin
ordering (an explicit ``reader`` may be passed to ``nImage`` directly).

Parameters
----------
Expand Down
57 changes: 57 additions & 0 deletions src/ndevio/_settings.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
"""Access to ndevio's plugin settings.

ndevio's user-configurable behavior is declared in ``napari.yaml`` under
``contributions.configurations`` (two categories: ``reader`` and ``export``)
and surfaces in napari's **Preferences** dialog. At runtime the values are
read through ``napari.settings.get_plugin_settings('ndevio')``.

On napari versions too old to expose plugin settings (i.e. it predates the
``get_plugin_settings`` API, released in 0.9.0), :func:`get_ndevio_settings`
falls back to the defaults declared in ``napari.yaml`` so ndevio keeps working
everywhere.
"""

from __future__ import annotations

from types import SimpleNamespace
from typing import TYPE_CHECKING

if TYPE_CHECKING:
from napari.settings import PluginPreferences

_PLUGIN_NAME = 'ndevio'

# Defaults mirroring the ``contributions.configurations`` block in
# ``napari.yaml`` — only the values ndevio's code reads. Used as a fallback
# when napari is too old to expose plugin settings; the settings are not
# user-configurable in that case.
_DEFAULTS = SimpleNamespace(
reader=SimpleNamespace(
suggest_reader_plugins=True,
scene_handling='Open Scene Widget',
clear_layers_on_new_scene=False,
max_in_mem_gb=8.0,
),
export=SimpleNamespace(
canvas_scale=1.0,
override_canvas_size=False,
canvas_width=1024,
canvas_height=1024,
),
)


def get_ndevio_settings() -> PluginPreferences | SimpleNamespace:
"""Return ndevio's plugin settings, falling back to manifest defaults.

Returns the napari-managed plugin preferences for ``ndevio`` when
available; otherwise a ``SimpleNamespace`` carrying the manifest defaults
(used when napari is too old for ``get_plugin_settings``). Both expose
the same ``.reader`` and ``.export`` attributes.
"""
try:
from napari.settings import get_plugin_settings

return get_plugin_settings(_PLUGIN_NAME)
except ImportError: # pragma: no cover - napari < 0.9.0
return _DEFAULTS
7 changes: 4 additions & 3 deletions src/ndevio/bioio_plugins/_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -159,13 +159,14 @@ def raise_unsupported_with_suggestions(path: PathLike) -> None:

"""
from bioio_base.exceptions import UnsupportedFileFormatError
from ndev_settings import get_settings

settings = get_settings()
from ndevio._settings import get_ndevio_settings

settings = get_ndevio_settings()
manager = ReaderPluginManager(path)
msg_extra = (
manager.get_installation_message()
if settings.ndevio_reader.suggest_reader_plugins # type: ignore
if settings.reader.suggest_reader_plugins
else None
)

Expand Down
71 changes: 71 additions & 0 deletions src/ndevio/napari.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,77 @@ contributions:
'*.txt', '*.v', '*.vms', '*.vsi', '*.vws', '*.wat', '*.wlz', '*.xdce', '*.xml', '*.xqd',
'*.xqf', '*.xv', '*.xvthumb', '*.xys', '*.zarr', '*.zarr*', '*.zfp', '*.zfr', '*.zif', '*.zvi',
]
configurations:
reader:
title: Reader
properties:
suggest_reader_plugins:
type: boolean
default: true
title: Suggest reader plugins
description: >-
Whether to suggest plugins to install when no reader can be found
for a file.
scene_handling:
type: string
default: Open Scene Widget
title: Scene handling
description: >-
How to handle files with multiple scenes. Choose between showing
a scene-selection widget, viewing all scenes as layers, or viewing
only the first scene.
enum:
- Open Scene Widget
- View All Scenes
- View First Scene Only
clear_layers_on_new_scene:
type: boolean
default: false
title: Clear layers on new scene
description: Whether to clear the viewer when selecting a new scene.
max_in_mem_gb:
type: number
default: 8.0
title: Max in-memory size (GB)
description: >-
Maximum uncompressed image size in GB to load eagerly. Larger
images use dask.
minimum: 0.5
maximum: 128.0
export:
title: Export
properties:
canvas_scale:
type: number
default: 1.0
title: Canvas scale
description: Scale factor for exported figures and screenshots.
minimum: 0.01
maximum: 100.0
override_canvas_size:
type: boolean
default: false
title: Override canvas size
description: >-
Whether to override the canvas size when exporting screenshots.
canvas_width:
type: integer
default: 1024
title: Canvas width
description: >-
Canvas width in pixels when exporting screenshots (if override
is enabled).
minimum: 1
maximum: 100000
canvas_height:
type: integer
default: 1024
title: Canvas height
description: >-
Canvas height in pixels when exporting screenshots (if override
is enabled).
minimum: 1
maximum: 100000
widgets:
- command: ndevio.make_plugin_installer_widget
display_name: Install BioIO Reader Plugins
Expand Down
51 changes: 0 additions & 51 deletions src/ndevio/ndev_settings.yaml

This file was deleted.

Loading