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
42 changes: 42 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,48 @@ print(settings.Canvas.canvas_scale)
# Access settings from external libraries (if installed)
print(settings.Reader.preferred_reader) # From ndevio
print(settings.Export.compression_level) # From ndevio

# Modify and save settings
settings.Canvas.canvas_scale = 2.0
settings.save() # Persists across sessions

# Reset to defaults
settings.reset_to_default("canvas_scale") # Reset single setting
settings.reset_to_default(group="Canvas") # Reset entire group
settings.reset_to_default() # Reset all settings
```

## Performance Note: npe1 Plugin Compatibility

If you have many legacy npe1 plugins installed (e.g., `napari-assistant`, `napari-segment-blobs-and-things-with-membranes`, `napari-simpleitk-image-processing`), you may experience slow widget loading times (10+ seconds) the first time you open the settings plugin widget in a napari session. This is a known issue in napari's npe1 compatibility layer, not specific to ndev-settings. The npe1 adapter iterates through all plugin widgets and performs expensive metadata lookups for each legacy plugin.

**Workaround**: If you don't need npe1 runtime behavior plugins, you can disable the adapter in napari:

1. Go to `File` -> `Preferences` -> `Plugins`
2. Uncheck "Use npe2 adapter"
3. Restart napari

This dramatically improves widget loading times since only pure npe2 plugins are discovered.

## How Settings Persistence Works

Settings are automatically cached to improve startup performance:

1. **First load**: Settings are discovered from all installed packages via entry points, merged together, and saved to a user config file
2. **Subsequent loads**: Settings are loaded directly from the cached file (much faster)
3. **Package changes**: When packages are installed/removed, settings are re-discovered and merged while preserving your customizations

**Storage location**: Settings are stored in a platform-appropriate config directory:

- **Windows**: `%LOCALAPPDATA%\ndev-settings\settings.yaml`
- **macOS**: `~/Library/Application Support/ndev-settings/settings.yaml`
- **Linux**: `~/.config/ndev-settings/settings.yaml`

**Clearing the cache**: To force re-discovery of settings (e.g., after manual edits to package YAML files):

```python
from ndev_settings import clear_settings
clear_settings() # Deletes cached settings, next load will re-discover
```

## Pre-commit hook
Expand Down
6 changes: 4 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ requires-python = ">=3.10"
# or any other Qt bindings directly (e.g. PyQt5, PySide2).
# See best practices: https://napari.org/stable/plugins/building_a_plugin/best_practices.html
dependencies = [
"appdirs",
"magicgui",
"magic-class",
"pyyaml",
Expand All @@ -37,15 +38,16 @@ dependencies = [
[project.optional-dependencies]
# Allow easily installation with the full, default napari installation
# (including Qt backend) using ndev-settings[all].
all = ["napari[all]"]
all = ["napari[pyqt6]"]

[dependency-groups]
testing = [
dev = [
"tox-uv",
"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/
"pyqt6",
"napari",
]

[project.entry-points."napari.manifest"]
Expand Down
4 changes: 2 additions & 2 deletions src/ndev_settings/__init__.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from ._settings import Settings
from ._settings import Settings, clear_settings
from ._version import version as __version__

__all__ = ["get_settings", "__version__"]
__all__ = ["get_settings", "clear_settings", "__version__"]

# Singleton instance
_settings_instance = None
Expand Down
19 changes: 15 additions & 4 deletions src/ndev_settings/_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@
def reset_values_to_defaults(settings_file: Path | str) -> bool:
"""Reset all 'value' fields to match 'default' fields in a settings YAML file.

Works with flat format settings files where groups are at the top level.
Metadata keys starting with '_' (e.g., _entry_points_hash) are preserved.

Parameters
----------
settings_file : Path | str
Expand All @@ -26,14 +29,20 @@ def reset_values_to_defaults(settings_file: Path | str) -> bool:
return False

with open(settings_file) as f:
settings = yaml.safe_load(f)
settings_data = yaml.load(f, Loader=yaml.FullLoader)

if not settings:
if not settings_data:
return False

modified = False

for group_name, group_settings in settings.items():
for group_name, group_settings in settings_data.items():
# Skip metadata keys (e.g., _entry_points_hash)
if group_name.startswith("_"):
continue
if not isinstance(group_settings, dict):
continue

for setting_name, setting_data in group_settings.items():
if (
isinstance(setting_data, dict)
Expand All @@ -50,7 +59,9 @@ def reset_values_to_defaults(settings_file: Path | str) -> bool:

if modified:
with open(settings_file, "w") as f:
yaml.dump(settings, f, default_flow_style=False, sort_keys=False)
yaml.dump(
settings_data, f, default_flow_style=False, sort_keys=False
)

return modified

Expand Down
Loading