Skip to content
Open
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.configuration` 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.configuration`) 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.configuration`),
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.configuration`` 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
Loading