Skip to content

Commit

Permalink
feat: Allow passing a config from command line (#257)
Browse files Browse the repository at this point in the history
* ENH: Allow passing a config from command line

* enh: address review

* test: update tests for new cli args

* test: use warnings instead of napari for pytest

* fixup

---------

Co-authored-by: Talley Lambert <[email protected]>
  • Loading branch information
ianhi and tlambert03 authored Feb 2, 2024
1 parent e5a6f9c commit 41a9943
Show file tree
Hide file tree
Showing 3 changed files with 55 additions and 5 deletions.
23 changes: 21 additions & 2 deletions src/napari_micromanager/__main__.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,33 @@
"""Run napari-micromanager as a script with ``python -m napari_micromanager``."""
from __future__ import annotations

import argparse
import sys
from typing import Sequence

def main() -> None:

def main(args: Sequence[str] | None = None) -> None:
"""Create a napari viewer and add the MicroManager plugin to it."""
if args is None:
args = sys.argv

parser = argparse.ArgumentParser(description="Enter string")
parser.add_argument(
"-c",
"--config",
type=str,
default=None,
help="Config file to load",
nargs="?",
)
parsed_args = parser.parse_args(args)

import napari

from napari_micromanager.main_window import MainWindow

viewer = napari.Viewer()
win = MainWindow(viewer)
win = MainWindow(viewer, config=parsed_args.config)
dw = viewer.window.add_dock_widget(win, name="MicroManager", area="top")
if hasattr(dw, "_close_btn"):
dw._close_btn = False
Expand Down
14 changes: 13 additions & 1 deletion src/napari_micromanager/main_window.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import contextlib
import logging
from typing import TYPE_CHECKING, Any, Callable
from warnings import warn

import napari
import napari.layers
Expand All @@ -14,6 +15,8 @@
from ._gui_objects._toolbar import MicroManagerToolbar

if TYPE_CHECKING:
from pathlib import Path

from pymmcore_plus.core.events._protocol import PSignalInstance


Expand All @@ -24,7 +27,9 @@
class MainWindow(MicroManagerToolbar):
"""The main napari-micromanager widget that gets added to napari."""

def __init__(self, viewer: napari.viewer.Viewer) -> None:
def __init__(
self, viewer: napari.viewer.Viewer, config: str | Path | None = None
) -> None:
super().__init__(viewer)

# get global CMMCorePlus instance
Expand All @@ -49,6 +54,13 @@ def __init__(self, viewer: napari.viewer.Viewer) -> None:
self.destroyed.connect(self._cleanup)
atexit.register(self._cleanup)

if config is not None:
try:
self._mmc.loadSystemConfiguration(config)
except FileNotFoundError:
# don't crash if the user passed an invalid config
warn(f"Config file {config} not found. Nothing loaded.", stacklevel=2)

def _cleanup(self) -> None:
for signal, slot in self._connections:
with contextlib.suppress(TypeError, RuntimeError):
Expand Down
23 changes: 21 additions & 2 deletions tests/test_main.py
Original file line number Diff line number Diff line change
@@ -1,18 +1,37 @@
from pathlib import Path
from unittest.mock import patch

import pytest
from napari_micromanager.__main__ import main
from pymmcore_plus import CMMCorePlus


def test_cli_main() -> None:
@pytest.mark.parametrize(
"argv",
[
[],
["--config", str(Path(__file__).parent / "test_config.cfg")],
["-c", "nonexistant"],
],
)
def test_cli_main(argv: list) -> None:
import napari
from napari.qt import QtViewer

with patch("napari.run") as mock_run:
with patch("qtpy.QtWidgets.QMainWindow.show") as mock_show:
main()
if "nonexistant" in argv:
with pytest.warns():
main(argv)
else:
main(argv)

mock_run.assert_called_once()
mock_show.assert_called_once()

if argv and "test_config" in argv[-1]:
assert len(CMMCorePlus.instance().getLoadedDevices()) > 1

# this is to prevent a leaked widget error in the NEXT test
napari.current_viewer().close()
QtViewer._instances.clear()

0 comments on commit 41a9943

Please sign in to comment.