diff --git a/.gitignore b/.gitignore index 871fb4d4..bf28739b 100644 --- a/.gitignore +++ b/.gitignore @@ -86,3 +86,6 @@ venv/ # Scripts /scripts/* !/scripts/examples + +# Ignore dynaconf secret files +.secrets.* diff --git a/dev-environment.yml b/dev-environment.yml index 0126c66c..7787150f 100644 --- a/dev-environment.yml +++ b/dev-environment.yml @@ -14,6 +14,7 @@ channels: - defaults dependencies: # Project dependencies + - dynaconf - labeling >= 0.1.12 - magicgui >= 0.5.1 - napari diff --git a/environment.yml b/environment.yml index c486111a..d08593e4 100644 --- a/environment.yml +++ b/environment.yml @@ -14,6 +14,7 @@ channels: - defaults dependencies: # Project depenencies + - dynaconf - labeling >= 0.1.12 - magicgui >= 0.5.1 - napari diff --git a/settings.toml b/settings.toml new file mode 100644 index 00000000..46feab23 --- /dev/null +++ b/settings.toml @@ -0,0 +1,18 @@ +# napari-imagej Settings + +# USERS BEWARE: +# This toml file will soon be replaced with napari's contribution configuration. + +# Path to a local ImageJ2 installation (e.g. /Applications/Fiji.app), +# OR version of net.imagej:imagej artifact to launch (e.g. 2.3.0), +# OR endpoint of another artifact built on ImageJ2 (e.g. sc.fiji:fiji), +# OR list of Maven artifacts to include (e.g. +# ['net.imagej:imagej:2.3.0', 'net.imagej:imagej-legacy', 'net.preibisch:BigStitcher']). +# The default is the latest version of ImageJ2. +imagej_directory_or_endpoint = "net.imagej:imagej" + +# This can be used to identify whether transferred data between ImageJ2 and napari +# should be selected via activation or by user selection via a dialog. +# By default, the active layer/window is chosen for transfer between applications. +# By setting this value to false, a popup will be shown instead. +choose_active_layer = true \ No newline at end of file diff --git a/settings.yml b/settings.yml deleted file mode 100644 index 468cc7ef..00000000 --- a/settings.yml +++ /dev/null @@ -1,15 +0,0 @@ -# napari-imagej Settings - -# USERS BEWARE: -# This yml file will soon be replaced with napari's contribution configuration. - -# This can be replaced with an ABSOLUTE path to a local ImageJ2 or Fiji installation. -# If left null, napari-imagej will instead launch a vanilla ImageJ2, downloading -# the latest available ImageJ2 if necessary. -imagej_installation: null - -# This can be used to identify whether transferred data between ImageJ2 and napari -# should be selected via activation or by user selection via a dialog. -# By default, the active layer/window is chosen for transfer between applications. -# By setting this value to false, a popup will be shown instead. -choose_active_layer: true \ No newline at end of file diff --git a/setup.cfg b/setup.cfg index 951d74e2..64a7daee 100644 --- a/setup.cfg +++ b/setup.cfg @@ -37,6 +37,7 @@ package_dir = # add your package requirements here install_requires = + dynaconf labeling >= 0.1.12 napari magicgui >= 0.5.1 diff --git a/src/napari_imagej/java.py b/src/napari_imagej/java.py index f4fc009a..e016032c 100644 --- a/src/napari_imagej/java.py +++ b/src/napari_imagej/java.py @@ -19,15 +19,14 @@ """ import os import sys -from functools import lru_cache from multiprocessing.pool import AsyncResult, ThreadPool -from typing import Any, Callable, Dict +from typing import Callable import imagej -import yaml from jpype import JClass from scyjava import config, jimport +from napari_imagej.settings import preferences from napari_imagej.utilities.logging import log_debug # -- ImageJ API -- # @@ -48,17 +47,6 @@ def ensure_jvm_started() -> None: ij_future.wait() -def setting(name: str): - """Gets the value of setting name""" - return settings().get(name, None) - - -@lru_cache(maxsize=None) -def settings() -> Dict[Any, Any]: - """Gets all plugin settings as a dictionary""" - return yaml.safe_load(open("settings.yml", "r")) - - def get_mode() -> str: """ Returns the mode ImageJ will be run in @@ -87,7 +75,7 @@ def _imagej_init(): # Configure PyImageJ settings settings = { - "ij_dir_or_version_or_endpoint": setting("imagej_installation"), + "ij_dir_or_version_or_endpoint": preferences.imagej_directory_or_endpoint, "mode": get_mode(), "add_legacy": False, } diff --git a/src/napari_imagej/settings.py b/src/napari_imagej/settings.py new file mode 100644 index 00000000..44f5aae1 --- /dev/null +++ b/src/napari_imagej/settings.py @@ -0,0 +1,8 @@ +from dynaconf import Dynaconf + +# Preferences settings + +preferences = Dynaconf( + envvar_prefix="DYNACONF", + settings_files=["settings.toml", ".secrets.toml"], +) diff --git a/src/napari_imagej/widgets/menu.py b/src/napari_imagej/widgets/menu.py index 96638e92..59973889 100644 --- a/src/napari_imagej/widgets/menu.py +++ b/src/napari_imagej/widgets/menu.py @@ -14,14 +14,8 @@ from qtpy.QtGui import QIcon, QPixmap from qtpy.QtWidgets import QHBoxLayout, QMessageBox, QPushButton, QWidget -from napari_imagej.java import ( - ensure_jvm_started, - ij, - jc, - log_debug, - running_headless, - setting, -) +from napari_imagej.java import ensure_jvm_started, ij, jc, log_debug, running_headless +from napari_imagej.settings import preferences from napari_imagej.utilities._module_utils import _get_layers_hack @@ -134,7 +128,7 @@ def __init__(self, viewer: Viewer): icon = QColoredSVGIcon.from_resources("long_right_arrow") self.setIcon(icon.colored(theme=viewer.theme)) self.setToolTip("Export active napari layer to ImageJ2") - if setting("choose_active_layer"): + if preferences.choose_active_layer: self.clicked.connect(self.send_active_layer) else: self.clicked.connect(self.send_chosen_layer) @@ -179,7 +173,7 @@ def __init__(self, viewer: Viewer): icon = QColoredSVGIcon.from_resources("long_left_arrow") self.setIcon(icon.colored(theme=viewer.theme)) self.setToolTip("Import active ImageJ2 Dataset to napari") - if setting("choose_active_layer"): + if preferences.choose_active_layer: self.clicked.connect(self.get_active_layer) else: self.clicked.connect(self.get_chosen_layer) diff --git a/tests/conftest.py b/tests/conftest.py index 18664f50..33fa6822 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -7,7 +7,7 @@ import pytest from napari import Viewer -from napari_imagej.widgets import menu +from napari_imagej.settings import preferences from napari_imagej.widgets.menu import NapariImageJMenu from napari_imagej.widgets.napari_imagej import NapariImageJWidget @@ -46,10 +46,8 @@ def gui_widget(viewer) -> Generator[NapariImageJMenu, None, None]: # Define GUIWidget settings for this particular feature. # In particular, we want to enforce active layer selection - def mock_setting(value: str): - return {"imagej_installation": None, "choose_active_layer": True}[value] - - menu.setting = mock_setting + previous = preferences.choose_active_layer + preferences.choose_active_layer = True # Create widget widget: NapariImageJMenu = NapariImageJMenu(viewer) @@ -58,6 +56,7 @@ def mock_setting(value: str): # Cleanup -> Close the widget, trigger ImageJ shutdown widget.close() + preferences.choose_active_layer = previous @pytest.fixture @@ -68,10 +67,8 @@ def gui_widget_chooser(viewer) -> Generator[NapariImageJMenu, None, None]: # Define GUIWidget settings for this particular feature. # In particular, we want to enforce user layer selection via Dialog - def mock_setting(value: str): - return {"imagej_installation": None, "choose_active_layer": False}[value] - - menu.setting = mock_setting + previous = preferences.choose_active_layer + preferences.choose_active_layer = False # Create widget widget: NapariImageJMenu = NapariImageJMenu(viewer) @@ -80,6 +77,7 @@ def mock_setting(value: str): # Cleanup -> Close the widget, trigger ImageJ shutdown widget.close() + preferences.choose_active_layer = previous @pytest.fixture()