diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index f2430370..4c9c7924 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -9,9 +9,6 @@ on:
- main
tags:
- "*-[0-9]+.*"
- pull_request:
- branches:
- - main
env:
NAPARI_IMAGEJ_TEST_TIMEOUT: 60000
diff --git a/doc/Configuration.rst b/doc/Configuration.rst
index c1d395a0..e4f21e21 100644
--- a/doc/Configuration.rst
+++ b/doc/Configuration.rst
@@ -95,6 +95,15 @@ One common use case for this feature is to increase the maximum heap space avail
Specifying 32GB of memory available to ImageJ ecosystem routines in the JVM.
+Using the SciJava REPL
+--------------------------------
+
+You can use the SciJava REPL to interactively run SciJava code. This makes it possible to do things like paste existing SciJava scripts into the REPL. More information on scripting in SciJava can be found `here `_.
+
+.. figure:: https://media.imagej.net/napari-imagej/scijava_repl.png
+
+ The REPL can be shown/hidden by clicking on the command prompt icon.
+
.. _Fiji: https://imagej.net/software/fiji/
.. _ImageJ2: https://imagej.net/software/imagej2/
diff --git a/src/napari_imagej/__init__.py b/src/napari_imagej/__init__.py
index d8a694ef..13f7a49d 100644
--- a/src/napari_imagej/__init__.py
+++ b/src/napari_imagej/__init__.py
@@ -20,5 +20,9 @@
import scyjava as sj
+from napari_imagej.model import NapariImageJ
+
__author__ = "ImageJ2 developers"
__version__ = sj.get_version("napari-imagej")
+
+nij = NapariImageJ()
diff --git a/src/napari_imagej/java.py b/src/napari_imagej/java.py
index b4a132d3..d9968067 100644
--- a/src/napari_imagej/java.py
+++ b/src/napari_imagej/java.py
@@ -4,23 +4,20 @@
Notable functions included in the module:
* init_ij()
- used to create the ImageJ instance.
- * ij()
- - used to access the ImageJ instance.
Notable fields included in the module:
* jc
- object whose fields are lazily-loaded Java Class instances.
"""
-from typing import Any, Callable, Dict
+from typing import Any, Dict
import imagej
-from jpype import JClass
-from scyjava import config, get_version, is_version_at_least, jimport, jvm_started
+from scyjava import config, get_version, is_version_at_least, jimport, JavaClasses
from napari_imagej import settings
from napari_imagej.utilities.logging import log_debug
-# -- Constants --
+# -- Constants -- #
minimum_versions = {
"io.scif:scifio": "0.45.0",
@@ -34,62 +31,46 @@
"sc.fiji:TrackMate": "7.11.0",
}
-# -- ImageJ API -- #
-
-_ij = None
-
-
-def ij():
- if _ij is None:
- raise Exception(
- "The ImageJ instance has not yet been initialized! Please run init_ij()"
- )
- return _ij
+# -- Public functions -- #
def init_ij() -> "jc.ImageJ":
"""
- Creates the ImageJ instance
+ Create an ImageJ2 gateway.
"""
- global _ij
- if _ij:
- return _ij
log_debug("Initializing ImageJ2")
- # determine whether imagej is already running
- imagej_already_initialized: bool = hasattr(imagej, "gateway") and imagej.gateway
-
# -- CONFIGURATION -- #
- # Configure napari-imagej
from napari_imagej.types.converters import install_converters
-
install_converters()
-
log_debug("Completed JVM Configuration")
# -- INITIALIZATION -- #
# Launch ImageJ
- if imagej_already_initialized:
- _ij = imagej.gateway
- else:
- _ij = imagej.init(**_configure_imagej())
+ ij = (
+ imagej.gateway
+ if hasattr(imagej, "gateway") and imagej.gateway
+ else imagej.init(**_configure_imagej())
+ )
# Log initialization
- log_debug(f"Initialized at version {_ij.getVersion()}")
+ log_debug(f"Initialized at version {ij.getVersion()}")
# -- VALIDATION -- #
# Validate PyImageJ
- _validate_imagej()
+ _validate_imagej(ij)
- return _ij
+ return ij
+# -- Private functions -- #
+
def _configure_imagej() -> Dict[str, Any]:
"""
- Configures scyjava and pyimagej.
+ Configure scyjava and pyimagej.
This function returns the settings that must be passed in the
actual initialization call.
@@ -112,9 +93,9 @@ def _configure_imagej() -> Dict[str, Any]:
return init_settings
-def _validate_imagej():
+def _validate_imagej(ij: "jc.ImageJ"):
"""
- Helper function to ensure minimum requirements on java component versions
+ Ensure minimum requirements on java component versions are met.
"""
# If we want to require a minimum version for a java component, we need to
# be able to find our current version. We do that by querying a Java class
@@ -131,7 +112,7 @@ def _validate_imagej():
"org.scijava:scijava-common": jc.Module,
"org.scijava:scijava-search": jc.Searcher,
}
- component_requirements.update(_optional_requirements())
+ component_requirements.update(_optional_requirements(ij))
# Find version that violate the minimum
violations = []
for component, cls in component_requirements.items():
@@ -153,11 +134,11 @@ def _validate_imagej():
raise RuntimeError(failure_str)
-def _optional_requirements():
+def _optional_requirements(ij: "jc.ImageJ"):
optionals = {}
# Add additional minimum versions for legacy components
- if _ij.legacy and _ij.legacy.isActive():
- optionals["net.imagej:imagej-legacy"] = _ij.legacy.getClass()
+ if ij.legacy and ij.legacy.isActive():
+ optionals["net.imagej:imagej-legacy"] = ij.legacy.getClass()
# Add additional minimum versions for fiji components
try:
optionals["sc.fiji:TrackMate"] = jimport("fiji.plugin.trackmate.TrackMate")
@@ -167,527 +148,517 @@ def _optional_requirements():
return optionals
-class JavaClasses(object):
- def blocking_import(func: Callable[[], str]) -> Callable[[], JClass]:
- """
- A decorator used to lazily evaluate a java import.
- func is a function of a Python class that takes no arguments and
- returns a string identifying a Java class by name.
-
- Using that function, this decorator creates a property
- that when called:
- * Blocks until the ImageJ gateway has been created
- * Imports the class identified by the function
- """
-
- @property
- def inner(self):
- if not jvm_started():
- raise Exception()
- try:
- return jimport(func(self))
- except TypeError:
- return None
-
- return inner
+class NijJavaClasses(JavaClasses):
# Java Primitives
- @blocking_import
+ @JavaClasses.java_import
def Boolean(self):
return "java.lang.Boolean"
- @blocking_import
+ @JavaClasses.java_import
def Byte(self):
return "java.lang.Byte"
- @blocking_import
+ @JavaClasses.java_import
def Class(self):
return "java.lang.Class"
- @blocking_import
+ @JavaClasses.java_import
def Character(self):
return "java.lang.Character"
- @blocking_import
+ @JavaClasses.java_import
def Double(self):
return "java.lang.Double"
- @blocking_import
+ @JavaClasses.java_import
def Float(self):
return "java.lang.Float"
- @blocking_import
+ @JavaClasses.java_import
+ def ImageJ(self):
+ return "net.imagej.ImageJ"
+
+ @JavaClasses.java_import
def Integer(self):
return "java.lang.Integer"
- @blocking_import
+ @JavaClasses.java_import
def Long(self):
return "java.lang.Long"
- @blocking_import
+ @JavaClasses.java_import
def Number(self):
return "java.lang.Number"
- @blocking_import
+ @JavaClasses.java_import
def Short(self):
return "java.lang.Short"
- @blocking_import
+ @JavaClasses.java_import
def String(self):
return "java.lang.String"
# Java Array Primitives
- @blocking_import
+ @JavaClasses.java_import
def Boolean_Arr(self):
return "[Z"
- @blocking_import
+ @JavaClasses.java_import
def Byte_Arr(self):
return "[B"
- @blocking_import
+ @JavaClasses.java_import
def Character_Arr(self):
return "[C"
- @blocking_import
+ @JavaClasses.java_import
def Double_Arr(self):
return "[D"
- @blocking_import
+ @JavaClasses.java_import
def Float_Arr(self):
return "[F"
- @blocking_import
+ @JavaClasses.java_import
def Integer_Arr(self):
return "[I"
- @blocking_import
+ @JavaClasses.java_import
def Long_Arr(self):
return "[J"
- @blocking_import
+ @JavaClasses.java_import
def Short_Arr(self):
return "[S"
# Vanilla Java Classes
- @blocking_import
+ @JavaClasses.java_import
def ArrayList(self):
return "java.util.ArrayList"
- @blocking_import
+ @JavaClasses.java_import
def BigDecimal(self):
return "java.math.BigDecimal"
- @blocking_import
+ @JavaClasses.java_import
def BigInteger(self):
return "java.math.BigInteger"
- @blocking_import
+ @JavaClasses.java_import
def Date(self):
return "java.util.Date"
- @blocking_import
+ @JavaClasses.java_import
def Enum(self):
return "java.lang.Enum"
- @blocking_import
+ @JavaClasses.java_import
def File(self):
return "java.io.File"
- @blocking_import
+ @JavaClasses.java_import
def HashMap(self):
return "java.util.HashMap"
- @blocking_import
+ @JavaClasses.java_import
def Path(self):
return "java.nio.file.Path"
- @blocking_import
+ @JavaClasses.java_import
def Window(self):
return "java.awt.Window"
+ @JavaClasses.java_import
+ def ScriptException(self):
+ return "javax.script.ScriptException"
+
# SciJava Types
- @blocking_import
+ @JavaClasses.java_import
def DisplayPostprocessor(self):
return "org.scijava.display.DisplayPostprocessor"
- @blocking_import
+ @JavaClasses.java_import
def FileWidget(self):
return "org.scijava.widget.FileWidget"
- @blocking_import
+ @JavaClasses.java_import
def InputHarvester(self):
return "org.scijava.widget.InputHarvester"
- @blocking_import
+ @JavaClasses.java_import
def Module(self):
return "org.scijava.module.Module"
- @blocking_import
+ @JavaClasses.java_import
def ModuleEvent(self):
return "org.scijava.module.event.ModuleEvent"
- @blocking_import
+ @JavaClasses.java_import
def ModuleCanceledEvent(self):
return "org.scijava.module.event.ModuleCanceledEvent"
- @blocking_import
+ @JavaClasses.java_import
def ModuleErroredEvent(self):
return "org.scijava.module.event.ModuleErroredEvent"
- @blocking_import
+ @JavaClasses.java_import
def ModuleExecutedEvent(self):
return "org.scijava.module.event.ModuleExecutedEvent"
- @blocking_import
+ @JavaClasses.java_import
def ModuleExecutingEvent(self):
return "org.scijava.module.event.ModuleExecutingEvent"
- @blocking_import
+ @JavaClasses.java_import
def ModuleFinishedEvent(self):
return "org.scijava.module.event.ModuleFinishedEvent"
- @blocking_import
+ @JavaClasses.java_import
def ModuleInfo(self):
return "org.scijava.module.ModuleInfo"
- @blocking_import
+ @JavaClasses.java_import
def ModuleItem(self):
return "org.scijava.module.ModuleItem"
- @blocking_import
+ @JavaClasses.java_import
def ModuleStartedEvent(self):
return "org.scijava.module.event.ModuleStartedEvent"
- @blocking_import
+ @JavaClasses.java_import
def PostprocessorPlugin(self):
return "org.scijava.module.process.PostprocessorPlugin"
- @blocking_import
+ @JavaClasses.java_import
def PreprocessorPlugin(self):
return "org.scijava.module.process.PreprocessorPlugin"
- @blocking_import
+ @JavaClasses.java_import
def ResultsPostprocessor(self):
return "org.scijava.table.process.ResultsPostprocessor"
- @blocking_import
+ @JavaClasses.java_import
def SciJavaEvent(self):
return "org.scijava.event.SciJavaEvent"
- @blocking_import
+ @JavaClasses.java_import
+ def ScriptREPL(self):
+ return "org.scijava.script.ScriptREPL"
+
+ @JavaClasses.java_import
def Searcher(self):
return "org.scijava.search.Searcher"
- @blocking_import
+ @JavaClasses.java_import
def SearchEvent(self):
return "org.scijava.search.SearchEvent"
- @blocking_import
+ @JavaClasses.java_import
def SearchListener(self):
return "org.scijava.search.SearchListener"
- @blocking_import
+ @JavaClasses.java_import
def SearchResult(self):
return "org.scijava.search.SearchResult"
- @blocking_import
+ @JavaClasses.java_import
def Table(self):
return "org.scijava.table.Table"
- @blocking_import
+ @JavaClasses.java_import
def Types(self):
return "org.scijava.util.Types"
- @blocking_import
+ @JavaClasses.java_import
def UIComponent(self):
return "org.scijava.widget.UIComponent"
- @blocking_import
+ @JavaClasses.java_import
def UIShownEvent(self):
return "org.scijava.ui.event.UIShownEvent"
- @blocking_import
+ @JavaClasses.java_import
def UserInterface(self):
return "org.scijava.ui.UserInterface"
# ImageJ Legacy Types
- @blocking_import
+ @JavaClasses.java_import
def LegacyCommandInfo(self):
return "net.imagej.legacy.command.LegacyCommandInfo"
# ImgLib2 Types
- @blocking_import
+ @JavaClasses.java_import
def BitType(self):
return "net.imglib2.type.logic.BitType"
- @blocking_import
+ @JavaClasses.java_import
def BooleanType(self):
return "net.imglib2.type.BooleanType"
- @blocking_import
+ @JavaClasses.java_import
def ColorTable(self):
return "net.imglib2.display.ColorTable"
- @blocking_import
+ @JavaClasses.java_import
def ColorTable8(self):
return "net.imglib2.display.ColorTable8"
- @blocking_import
+ @JavaClasses.java_import
def ColorTables(self):
return "net.imagej.display.ColorTables"
- @blocking_import
+ @JavaClasses.java_import
def ComplexType(self):
return "net.imglib2.type.numeric.ComplexType"
- @blocking_import
+ @JavaClasses.java_import
def DoubleType(self):
return "net.imglib2.type.numeric.real.DoubleType"
- @blocking_import
+ @JavaClasses.java_import
def Img(self):
return "net.imglib2.img.Img"
- @blocking_import
+ @JavaClasses.java_import
def IntegerType(self):
return "net.imglib2.type.numeric.IntegerType"
- @blocking_import
+ @JavaClasses.java_import
def IterableInterval(self):
return "net.imglib2.IterableInterval"
- @blocking_import
+ @JavaClasses.java_import
def LongType(self):
return "net.imglib2.type.numeric.integer.LongType"
- @blocking_import
+ @JavaClasses.java_import
def NumericType(self):
return "net.imglib2.type.numeric.NumericType"
- @blocking_import
+ @JavaClasses.java_import
def OutOfBoundsFactory(self):
return "net.imglib2.outofbounds.OutOfBoundsFactory"
- @blocking_import
+ @JavaClasses.java_import
def OutOfBoundsBorderFactory(self):
return "net.imglib2.outofbounds.OutOfBoundsBorderFactory"
- @blocking_import
+ @JavaClasses.java_import
def OutOfBoundsMirrorExpWindowingFactory(self):
return "net.imglib2.outofbounds.OutOfBoundsMirrorExpWindowingFactory"
- @blocking_import
+ @JavaClasses.java_import
def OutOfBoundsMirrorFactory(self):
return "net.imglib2.outofbounds.OutOfBoundsMirrorFactory"
- @blocking_import
+ @JavaClasses.java_import
def OutOfBoundsPeriodicFactory(self):
return "net.imglib2.outofbounds.OutOfBoundsPeriodicFactory"
- @blocking_import
+ @JavaClasses.java_import
def OutOfBoundsRandomValueFactory(self):
return "net.imglib2.outofbounds.OutOfBoundsRandomValueFactory"
- @blocking_import
+ @JavaClasses.java_import
def RandomAccessible(self):
return "net.imglib2.RandomAccessible"
- @blocking_import
+ @JavaClasses.java_import
def RandomAccessibleInterval(self):
return "net.imglib2.RandomAccessibleInterval"
- @blocking_import
+ @JavaClasses.java_import
def RealPoint(self):
return "net.imglib2.RealPoint"
- @blocking_import
+ @JavaClasses.java_import
def RealType(self):
return "net.imglib2.type.numeric.RealType"
# ImgLib2-algorithm Types
- @blocking_import
+ @JavaClasses.java_import
def CenteredRectangleShape(self):
return "net.imglib2.algorithm.neighborhood.CenteredRectangleShape"
- @blocking_import
+ @JavaClasses.java_import
def DiamondShape(self):
return "net.imglib2.algorithm.neighborhood.DiamondShape"
- @blocking_import
+ @JavaClasses.java_import
def DiamondTipsShape(self):
return "net.imglib2.algorithm.neighborhood.DiamondTipsShape"
- @blocking_import
+ @JavaClasses.java_import
def HorizontalLineShape(self):
return "net.imglib2.algorithm.neighborhood.HorizontalLineShape"
- @blocking_import
+ @JavaClasses.java_import
def HyperSphereShape(self):
return "net.imglib2.algorithm.neighborhood.HyperSphereShape"
- @blocking_import
+ @JavaClasses.java_import
def PairOfPointsShape(self):
return "net.imglib2.algorithm.neighborhood.PairOfPointsShape"
- @blocking_import
+ @JavaClasses.java_import
def PeriodicLineShape(self):
return "net.imglib2.algorithm.neighborhood.PeriodicLineShape"
- @blocking_import
+ @JavaClasses.java_import
def RectangleShape(self):
return "net.imglib2.algorithm.neighborhood.RectangleShape"
- @blocking_import
+ @JavaClasses.java_import
def Shape(self):
return "net.imglib2.algorithm.neighborhood.Shape"
# ImgLib2-roi Types
- @blocking_import
+ @JavaClasses.java_import
def Box(self):
return "net.imglib2.roi.geom.real.Box"
- @blocking_import
+ @JavaClasses.java_import
def ClosedWritableBox(self):
return "net.imglib2.roi.geom.real.ClosedWritableBox"
- @blocking_import
+ @JavaClasses.java_import
def ClosedWritableEllipsoid(self):
return "net.imglib2.roi.geom.real.ClosedWritableEllipsoid"
- @blocking_import
+ @JavaClasses.java_import
def ClosedWritablePolygon2D(self):
return "net.imglib2.roi.geom.real.ClosedWritablePolygon2D"
- @blocking_import
+ @JavaClasses.java_import
def DefaultWritableLine(self):
return "net.imglib2.roi.geom.real.DefaultWritableLine"
- @blocking_import
+ @JavaClasses.java_import
def DefaultWritablePolyline(self):
return "net.imglib2.roi.geom.real.DefaultWritablePolyline"
- @blocking_import
+ @JavaClasses.java_import
def DefaultWritableRealPointCollection(self):
return "net.imglib2.roi.geom.real.DefaultWritableRealPointCollection"
- @blocking_import
+ @JavaClasses.java_import
def ImgLabeling(self):
return "net.imglib2.roi.labeling.ImgLabeling"
- @blocking_import
+ @JavaClasses.java_import
def Line(self):
return "net.imglib2.roi.geom.real.Line"
- @blocking_import
+ @JavaClasses.java_import
def PointMask(self):
return "net.imglib2.roi.geom.real.PointMask"
- @blocking_import
+ @JavaClasses.java_import
def Polygon2D(self):
return "net.imglib2.roi.geom.real.Polygon2D"
- @blocking_import
+ @JavaClasses.java_import
def Polyline(self):
return "net.imglib2.roi.geom.real.Polyline"
- @blocking_import
+ @JavaClasses.java_import
def RealPointCollection(self):
return "net.imglib2.roi.geom.real.RealPointCollection"
- @blocking_import
+ @JavaClasses.java_import
def SuperEllipsoid(self):
return "net.imglib2.roi.geom.real.SuperEllipsoid"
# ImageJ2 Types
- @blocking_import
+ @JavaClasses.java_import
def Axes(self):
return "net.imagej.axis.Axes"
- @blocking_import
+ @JavaClasses.java_import
def Dataset(self):
return "net.imagej.Dataset"
- @blocking_import
+ @JavaClasses.java_import
def DatasetView(self):
return "net.imagej.display.DatasetView"
- @blocking_import
+ @JavaClasses.java_import
def DefaultLinearAxis(self):
return "net.imagej.axis.DefaultLinearAxis"
- @blocking_import
+ @JavaClasses.java_import
def DefaultROITree(self):
return "net.imagej.roi.DefaultROITree"
- @blocking_import
+ @JavaClasses.java_import
def EnumeratedAxis(self):
return "net.imagej.axis.EnumeratedAxis"
- @blocking_import
+ @JavaClasses.java_import
def ImageDisplay(self):
return "net.imagej.display.ImageDisplay"
- @blocking_import
+ @JavaClasses.java_import
def ImgPlus(self):
return "net.imagej.ImgPlus"
- @blocking_import
+ @JavaClasses.java_import
def Mesh(self):
return "net.imagej.mesh.Mesh"
- @blocking_import
+ @JavaClasses.java_import
def NaiveDoubleMesh(self):
return "net.imagej.mesh.naive.NaiveDoubleMesh"
- @blocking_import
+ @JavaClasses.java_import
def ROITree(self):
return "net.imagej.roi.ROITree"
# ImageJ Types
- @blocking_import
+ @JavaClasses.java_import
def ImagePlus(self):
return "ij.ImagePlus"
- @blocking_import
+ @JavaClasses.java_import
def Roi(self):
return "ij.gui.Roi"
# ImageJ-Legacy Types
- @blocking_import
+ @JavaClasses.java_import
def IJRoiWrapper(self):
return "net.imagej.legacy.convert.roi.IJRoiWrapper"
# ImageJ-Ops Types
- @blocking_import
+ @JavaClasses.java_import
def Initializable(self):
return "net.imagej.ops.Initializable"
- @blocking_import
+ @JavaClasses.java_import
def OpInfo(self):
return "net.imagej.ops.OpInfo"
- @blocking_import
+ @JavaClasses.java_import
def OpSearcher(self):
return "net.imagej.ops.search.OpSearcher"
# Scifio-Labeling Types
- @blocking_import
+ @JavaClasses.java_import
def LabelingIOService(self):
return "io.scif.labeling.LabelingIOService"
-jc = JavaClasses()
+jc = NijJavaClasses()
diff --git a/src/napari_imagej/model.py b/src/napari_imagej/model.py
new file mode 100644
index 00000000..4437168e
--- /dev/null
+++ b/src/napari_imagej/model.py
@@ -0,0 +1,40 @@
+from jpype import JImplements, JOverride
+
+from napari_imagej.java import init_ij, jc
+
+
+class NapariImageJ:
+ """
+ An object offering a central access point to napari-imagej's core business logic.
+ """
+ def __init__(self):
+ self._ij = None
+ self._repl = None
+ self._repl_callbacks = []
+
+ @property
+ def ij(self):
+ if self._ij is None:
+ self._ij = init_ij()
+ return self._ij
+
+ @property
+ def repl(self) -> "jc.ScriptREPL":
+ if self._repl is None:
+ ctx = self.ij.context()
+ model = self
+
+ @JImplements("java.util.consumer.Consumer")
+ class REPLOutput:
+ @JOverride
+ def accept(self, t):
+ s = str(t)
+ for callback in model._repl_callbacks:
+ callback(s)
+
+ self._repl = jc.ScriptREPL(ctx, "jython", REPLOutput())
+ self._repl.lang("jython")
+ return self._repl
+
+ def add_repl_callback(self, repl_callback) -> None:
+ self._repl_callbacks.append(repl_callback)
diff --git a/src/napari_imagej/readers/trackMate_reader.py b/src/napari_imagej/readers/trackMate_reader.py
index 1e04719d..1ac84e33 100644
--- a/src/napari_imagej/readers/trackMate_reader.py
+++ b/src/napari_imagej/readers/trackMate_reader.py
@@ -6,7 +6,8 @@
from napari.utils import progress
from scyjava import jimport
-from napari_imagej.java import ij, init_ij, jc
+from napari_imagej import nij
+from napari_imagej.java import jc
from napari_imagej.types.converters.trackmate import (
model_and_image_to_tracks,
trackmate_present,
@@ -39,7 +40,7 @@ def napari_get_reader(path):
def reader_function(path):
pbr = progress(total=4, desc="Importing TrackMate XML: Starting JVM")
- init_ij()
+ ij = nij.ij
TmXMLReader = jimport("fiji.plugin.trackmate.io.TmXmlReader")
pbr.update()
@@ -50,7 +51,7 @@ def reader_function(path):
pbr.update()
pbr.set_description("Importing TrackMate XML: Converting Image")
- py_imp = ij().py.from_java(imp)
+ py_imp = ij.py.from_java(imp)
pbr.update()
pbr.set_description("Importing TrackMate XML: Converting Tracks and ROIs")
diff --git a/src/napari_imagej/resources/repl.svg b/src/napari_imagej/resources/repl.svg
new file mode 100644
index 00000000..c6a785bc
--- /dev/null
+++ b/src/napari_imagej/resources/repl.svg
@@ -0,0 +1,46 @@
+
+
+
+
diff --git a/src/napari_imagej/types/converters/images.py b/src/napari_imagej/types/converters/images.py
index 0c2e00f4..8b005e37 100644
--- a/src/napari_imagej/types/converters/images.py
+++ b/src/napari_imagej/types/converters/images.py
@@ -13,13 +13,14 @@
from scyjava import Priority
from xarray import DataArray
-from napari_imagej.java import ij, jc
+from napari_imagej import nij
+from napari_imagej.java import jc
from napari_imagej.types.converters import java_to_py_converter, py_to_java_converter
from napari_imagej.utilities.logging import log_debug
@java_to_py_converter(
- predicate=lambda obj: ij().convert().supports(obj, jc.DatasetView),
+ predicate=lambda obj: nij.ij.convert().supports(obj, jc.DatasetView),
priority=Priority.VERY_HIGH + 1,
)
def _java_image_to_image_layer(image: Any) -> Image:
@@ -33,9 +34,9 @@ def _java_image_to_image_layer(image: Any) -> Image:
:return: a napari Image layer
"""
# Construct a DatasetView from the Java image
- view = ij().convert().convert(image, jc.DatasetView)
+ view = nij.ij.convert().convert(image, jc.DatasetView)
# Construct an xarray from the DatasetView
- xarr: DataArray = java_to_xarray(ij(), view.getData())
+ xarr: DataArray = java_to_xarray(nij.ij, view.getData())
# Construct a map of Image layer parameters
kwargs = dict(
data=xarr,
@@ -59,7 +60,7 @@ def _image_layer_to_dataset(image: Image, **kwargs) -> "jc.Dataset":
:return: a Dataset
"""
# Construct a dataset from the data
- dataset: "jc.Dataset" = ij().py.to_dataset(image.data, **kwargs)
+ dataset: "jc.Dataset" = nij.ij.py.to_dataset(image.data, **kwargs)
# Clean up the axes
axes = [
@@ -94,7 +95,7 @@ def _image_layer_to_dataset(image: Image, **kwargs) -> "jc.Dataset":
properties = dataset.getProperties()
for k, v in image.metadata.items():
try:
- properties.put(ij().py.to_java(k), ij().py.to_java(v))
+ properties.put(nij.ij.py.to_java(k), nij.ij.py.to_java(v))
except Exception:
log_debug(f"Could not add property ({k}, {v}) to dataset {dataset}:")
return dataset
diff --git a/src/napari_imagej/types/converters/labels.py b/src/napari_imagej/types/converters/labels.py
index 4126c36c..54e647b0 100644
--- a/src/napari_imagej/types/converters/labels.py
+++ b/src/napari_imagej/types/converters/labels.py
@@ -7,7 +7,8 @@
from napari.layers import Labels
from scyjava import Priority
-from napari_imagej.java import ij, jc
+from napari_imagej import nij
+from napari_imagej.java import jc
from napari_imagej.types.converters import java_to_py_converter, py_to_java_converter
@@ -41,7 +42,7 @@ def _imglabeling_to_layer(imgLabeling: "jc.ImgLabeling") -> Labels:
:param imgLabeling: the Java ImgLabeling
:return: a Labels layer
"""
- labeling: Labeling = imglabeling_to_labeling(ij(), imgLabeling)
+ labeling: Labeling = imglabeling_to_labeling(nij.ij, imgLabeling)
return _labeling_to_layer(labeling)
@@ -55,4 +56,4 @@ def _layer_to_imglabeling(layer: Labels) -> "jc.ImgLabeling":
:return: the Java ImgLabeling
"""
labeling: Labeling = _layer_to_labeling(layer)
- return ij().py.to_java(labeling)
+ return nij.ij.py.to_java(labeling)
diff --git a/src/napari_imagej/types/converters/trackmate.py b/src/napari_imagej/types/converters/trackmate.py
index 6ea707f0..5090831b 100644
--- a/src/napari_imagej/types/converters/trackmate.py
+++ b/src/napari_imagej/types/converters/trackmate.py
@@ -4,10 +4,9 @@
import numpy as np
from napari.layers import Labels, Tracks
-from scyjava import Priority
+from scyjava import JavaClasses, Priority
-from napari_imagej import settings
-from napari_imagej.java import JavaClasses, ij
+from napari_imagej import nij, settings
from napari_imagej.types.converters import java_to_py_converter
@@ -36,7 +35,7 @@ def track_overlay_predicate(obj):
if not trackmate_present():
return False
# TrackMate data is wrapped in ImageJ Rois - we need ImageJ Legacy
- if not (ij().legacy and ij().legacy.isActive()):
+ if not (nij.ij.legacy and nij.ij.legacy.isActive()):
return False
# TrackMate data will be wrapped within a ROITree
if not isinstance(obj, jc.ROITree):
@@ -106,7 +105,7 @@ def model_and_image_to_tracks(model: "jc.Model", imp: "jc.ImagePlus"):
java_label_img = jc.LabelImgExporter.createLabelImagePlus(
model, imp, False, False, False
)
- py_label_img = ij().py.from_java(java_label_img)
+ py_label_img = nij.ij.py.from_java(java_label_img)
labels = Labels(data=py_label_img.data, name=rois_name)
return (tracks, labels)
@@ -119,7 +118,7 @@ def _trackMate_model_to_tracks(obj: "jc.ROITree"):
"""
Converts a TrackMate overlay into a napari Tracks layer
"""
- trackmate_plugins = ij().object().getObjects(jc.TrackMate)
+ trackmate_plugins = nij.ij.object().getObjects(jc.TrackMate)
if len(trackmate_plugins) == 0:
raise IndexError("Expected a TrackMate instance, but there was none!")
model: jc.Model = trackmate_plugins[-1].getModel()
@@ -130,39 +129,39 @@ def _trackMate_model_to_tracks(obj: "jc.ROITree"):
class TrackMateClasses(JavaClasses):
# TrackMate Types
- @JavaClasses.blocking_import
+ @JavaClasses.java_import
def BranchTableView(self):
return "fiji.plugin.trackmate.visualization.table.BranchTableView"
- @JavaClasses.blocking_import
+ @JavaClasses.java_import
def ConvexBranchesDecomposition(self):
return "fiji.plugin.trackmate.graph.ConvexBranchesDecomposition"
- @JavaClasses.blocking_import
+ @JavaClasses.java_import
def LabelImgExporter(self):
return "fiji.plugin.trackmate.action.LabelImgExporter"
- @JavaClasses.blocking_import
+ @JavaClasses.java_import
def Model(self):
return "fiji.plugin.trackmate.Model"
- @JavaClasses.blocking_import
+ @JavaClasses.java_import
def Spot(self):
return "fiji.plugin.trackmate.Spot"
- @JavaClasses.blocking_import
+ @JavaClasses.java_import
def SpotOverlay(self):
return "fiji.plugin.trackmate.visualization.hyperstack.SpotOverlay"
- @JavaClasses.blocking_import
+ @JavaClasses.java_import
def TMUtils(self):
return "fiji.plugin.trackmate.util.TMUtils"
- @JavaClasses.blocking_import
+ @JavaClasses.java_import
def TrackMate(self):
return "fiji.plugin.trackmate.TrackMate"
- @JavaClasses.blocking_import
+ @JavaClasses.java_import
def TrackOverlay(self):
return "fiji.plugin.trackmate.visualization.hyperstack.TrackOverlay"
diff --git a/src/napari_imagej/types/type_conversions.py b/src/napari_imagej/types/type_conversions.py
index ba8c60ec..a627e978 100644
--- a/src/napari_imagej/types/type_conversions.py
+++ b/src/napari_imagej/types/type_conversions.py
@@ -21,7 +21,8 @@
from jpype import JObject
from scyjava import Priority
-from napari_imagej.java import ij, jc
+from napari_imagej import nij
+from napari_imagej.java import jc
from napari_imagej.types.enum_likes import enum_like
from napari_imagej.types.enums import py_enum_for
from napari_imagej.types.type_hints import type_hints
@@ -201,6 +202,6 @@ def canConvertChecker(item: "jc.ModuleItem") -> Optional[Type]:
"""
def isAssignable(from_type, to_type) -> bool:
- return ij().convert().supports(from_type, to_type)
+ return nij.ij.convert().supports(from_type, to_type)
return _checkerUsingFunc(item, isAssignable)
diff --git a/src/napari_imagej/utilities/_module_utils.py b/src/napari_imagej/utilities/_module_utils.py
index f259d640..17d70be0 100644
--- a/src/napari_imagej/utilities/_module_utils.py
+++ b/src/napari_imagej/utilities/_module_utils.py
@@ -21,7 +21,8 @@
from pandas import DataFrame
from scyjava import JavaIterable, JavaList, JavaMap, JavaSet, is_arraylike, jstacktrace
-from napari_imagej.java import ij, jc
+from napari_imagej import nij
+from napari_imagej.java import jc
from napari_imagej.types.type_conversions import type_hint_for
from napari_imagej.types.type_utils import type_displayable_in_napari
from napari_imagej.types.widget_mappings import preferred_widget_for
@@ -42,7 +43,7 @@ def _preprocess_to_harvester(module) -> List["jc.PreprocessorPlugin"]:
:return: The list of preprocessors that have not yet run.
"""
- preprocessors = ij().plugin().createInstancesOfType(jc.PreprocessorPlugin)
+ preprocessors = nij.ij.plugin().createInstancesOfType(jc.PreprocessorPlugin)
for i, preprocessor in enumerate(preprocessors):
# if preprocessor is an InputHarvester, stop and return the remaining list
if isinstance(preprocessor, jc.InputHarvester):
@@ -189,7 +190,7 @@ def _param_default_or_none(input: "jc.ModuleItem") -> Optional[Any]:
# Parameter uses an internal type to denote a required parameter.
return _empty
try:
- return ij().py.from_java(default)
+ return nij.ij.py.from_java(default)
except Exception:
return default
@@ -314,7 +315,7 @@ def _pure_module_outputs(
continue
_handle_output(
- ij().py.from_java(output_entry.getValue()),
+ nij.ij.py.from_java(output_entry.getValue()),
_devise_layer_name(info, name),
info,
layer_outputs,
@@ -358,7 +359,7 @@ def _add_napari_metadata(
info: "jc.ModuleInfo",
unresolved_inputs: List["jc.ModuleItem"],
) -> None:
- module_name = ij().py.from_java(info.getTitle())
+ module_name = nij.ij.py.from_java(info.getTitle())
execute_module.__doc__ = f"Invoke ImageJ2's {module_name}"
execute_module.__name__ = module_name
execute_module.__qualname__ = module_name
@@ -389,7 +390,7 @@ def _add_param_metadata(metadata: dict, key: str, value: Any) -> None:
if value is None:
return
try:
- py_value = ij().py.from_java(value)
+ py_value = nij.ij.py.from_java(value)
if isinstance(py_value, JavaMap):
py_value = dict(py_value)
elif isinstance(py_value, JavaSet):
@@ -408,7 +409,7 @@ def _add_scijava_metadata(
) -> Dict[str, Dict[str, Any]]:
metadata = {}
for input in unresolved_inputs:
- key = ij().py.from_java(input.getName())
+ key = nij.ij.py.from_java(input.getName())
param_map = {}
_add_param_metadata(param_map, "max", input.getMaximumValue())
_add_param_metadata(param_map, "min", input.getMinimumValue())
@@ -439,7 +440,7 @@ def _get_postprocessors():
on SciJava Modules from napari-imagej
"""
# Discover all postprocessors
- postprocessors = ij().plugin().createInstancesOfType(jc.PostprocessorPlugin)
+ postprocessors = nij.ij.plugin().createInstancesOfType(jc.PostprocessorPlugin)
problematic_postprocessors = (
# HACK: This particular postprocessor is trying to create a Display
@@ -498,7 +499,7 @@ def module_execute(
start_time = perf_counter()
# Create user input map
- resolved_java_args = ij().py.jargs(*user_resolved_inputs)
+ resolved_java_args = nij.ij.py.jargs(*user_resolved_inputs)
input_map = jc.HashMap()
for module_item, input in zip(unresolved_inputs, resolved_java_args):
input_map.put(module_item.getName(), input)
@@ -522,7 +523,7 @@ def module_execute(
# before the module can update it through its own execution.
pm.init_progress(module)
# Run the module asynchronously using the ModuleService
- ij().module().run(
+ nij.ij.module().run(
module,
remaining_preprocessors,
postprocessors,
@@ -652,7 +653,7 @@ def process(self, module: "jc.Module"):
"display_results_in_new_window",
)
if display_externally is not None and len(widget_outputs) > 0:
- name = "Result: " + ij().py.from_java(module.getInfo().getTitle())
+ name = "Result: " + nij.ij.py.from_java(module.getInfo().getTitle())
self.output_handler(
{"data": widget_outputs, "name": name, "external": display_externally}
)
diff --git a/src/napari_imagej/utilities/event_subscribers.py b/src/napari_imagej/utilities/event_subscribers.py
index 758ff8d0..26ff9572 100644
--- a/src/napari_imagej/utilities/event_subscribers.py
+++ b/src/napari_imagej/utilities/event_subscribers.py
@@ -5,7 +5,8 @@
from jpype import JImplements, JOverride
from qtpy.QtCore import Signal
-from napari_imagej.java import ij, jc
+from napari_imagej import nij
+from napari_imagej.java import jc
from napari_imagej.utilities.logging import log_debug
@@ -51,7 +52,7 @@ def __init__(self):
def onEvent(self, event):
if not self.initialized:
# add our custom settings to the User Interface
- if ij().legacy and ij().legacy.isActive():
+ if nij.ij.legacy and nij.ij.legacy.isActive():
self._ij1_UI_setup()
self._ij2_UI_setup(event.getUI())
self.initialized = True
@@ -66,7 +67,7 @@ def equals(self, other):
def _ij1_UI_setup(self):
"""Configure the ImageJ Legacy GUI"""
- ij().IJ.getInstance().exitWhenQuitting(False)
+ nij.ij.IJ.getInstance().exitWhenQuitting(False)
def _ij2_UI_setup(self, ui: "jc.UserInterface"):
"""Configure the ImageJ2 Swing GUI behavior"""
@@ -119,5 +120,5 @@ def windowDeactivated(self, event):
pass
listener = NapariAdapter()
- ij().object().addObject(listener)
+ nij.ij.object().addObject(listener)
window.addWindowListener(listener)
diff --git a/src/napari_imagej/widgets/menu.py b/src/napari_imagej/widgets/menu.py
index ca72ff01..1b527282 100644
--- a/src/napari_imagej/widgets/menu.py
+++ b/src/napari_imagej/widgets/menu.py
@@ -13,13 +13,15 @@
from qtpy.QtWidgets import QHBoxLayout, QMessageBox, QPushButton, QWidget
from scyjava import is_arraylike
+from napari_imagej import nij
from napari_imagej import settings
-from napari_imagej.java import ij
from napari_imagej.resources import resource_path
from napari_imagej.utilities.event_subscribers import UIShownListener
from napari_imagej.utilities.events import subscribe, unsubscribe
from napari_imagej.widgets.widget_utils import _IMAGE_LAYER_TYPES, DetailExportDialog
+from napari_imagej.widgets.repl import REPLWidget
+
class NapariImageJMenu(QWidget):
"""Container widget comprising the napari-imagej menu bar."""
@@ -41,26 +43,31 @@ def __init__(self, viewer: Viewer):
self.gui_button: GUIButton = GUIButton(viewer)
self.layout().addWidget(self.gui_button)
+ self.repl_button: REPLButton = REPLButton(viewer)
+ self.repl_button.setToolTip("Show/hide the SciJava REPL")
+ self.layout().addWidget(self.repl_button)
+
self.settings_button: SettingsButton = SettingsButton(viewer)
+ self.settings_button.setToolTip("Show napari-imagej settings")
self.layout().addWidget(self.settings_button)
if settings.headless():
self.gui_button.clicked.connect(self.gui_button.disable_popup)
else:
- # NB We need to call ij().ui().showUI() on the GUI thread.
+ # NB We need to call nij.ij.ui().showUI() on the GUI thread.
# TODO: Use PyImageJ functionality
# see https://github.com/imagej/pyimagej/pull/260
def show_ui():
- if ij().ui().isVisible():
- ij().thread().queue(
- lambda: ij()
+ if nij.ij.ui().isVisible():
+ nij.ij.thread().queue(
+ lambda: nij.ij
.ui()
.getDefaultUI()
.getApplicationFrame()
.setVisible(True)
)
else:
- ij().thread().queue(lambda: ij().ui().showUI())
+ nij.ij.thread().queue(lambda: nij.ij.ui().showUI())
self.gui_button.clicked.connect(show_ui)
@@ -71,14 +78,18 @@ def finalize(self):
self.gui_button.setIcon(self.gui_button._icon())
self.gui_button.setEnabled(True)
self.gui_button.setToolTip("Display ImageJ2 UI")
+
+ # Now the REPL can be enabled
+ self.repl_button.setEnabled(True)
+
# Subscribe UIShownListener
self.subscriber = UIShownListener()
- subscribe(ij(), self.subscriber)
+ subscribe(nij.ij, self.subscriber)
def __del__(self):
# Unsubscribe UIShownListener
if self.subscriber:
- unsubscribe(ij(), self.subscriber)
+ unsubscribe(nij.ij, self.subscriber)
class IJMenuButton(QPushButton):
@@ -151,7 +162,7 @@ def send_active_layer(self):
if layer:
# Queue UI call on the EDT
# TODO: Use EventQueue.invokeLater scyjava wrapper, once it exists
- ij().thread().queue(lambda: ij().ui().show(ij().py.to_java(layer)))
+ nij.ij.thread().queue(lambda: nij.ij.ui().show(nij.ij.py.to_java(layer)))
else:
self.handle_no_choices()
@@ -177,20 +188,20 @@ def _icon(self):
return QColoredSVGIcon(resource_path("import"))
def _get_objects(self, t):
- compatibleInputs = ij().convert().getCompatibleInputs(t)
- compatibleInputs.addAll(ij().object().getObjects(t))
+ compatibleInputs = nij.ij.convert().getCompatibleInputs(t)
+ compatibleInputs.addAll(nij.ij.object().getObjects(t))
return list(compatibleInputs)
def get_active_layer(self) -> None:
# HACK: Sync ImagePlus before transferring
# This code can be removed once
# https://github.com/imagej/imagej-legacy/issues/286 is solved.
- if ij().legacy and ij().legacy.isActive():
- current_image_plus = ij().WindowManager.getCurrentImage()
+ if nij.ij.legacy and nij.ij.legacy.isActive():
+ current_image_plus = nij.ij.WindowManager.getCurrentImage()
if current_image_plus is not None:
- ij().py.sync_image(current_image_plus)
+ nij.ij.py.sync_image(current_image_plus)
# Get the active view from the active image display
- ids = ij().get("net.imagej.display.ImageDisplayService")
+ ids = nij.ij.get("net.imagej.display.ImageDisplayService")
# TODO: simplify to no-args once
# https://github.com/imagej/imagej-legacy/pull/287 is merged.
view = ids.getActiveDatasetView(ids.getActiveImageDisplay())
@@ -201,7 +212,7 @@ def get_active_layer(self) -> None:
def _add_layer(self, view):
# Convert the object into Python
- py_image = ij().py.from_java(view)
+ py_image = nij.ij.py.from_java(view)
# Create and add the layer
if isinstance(py_image, Layer):
self.viewer.add_layer(py_image)
@@ -216,7 +227,7 @@ def _add_layer(self, view):
self.viewer.add_layer(itm)
# Other
elif is_arraylike(py_image):
- name = ij().object().getName(view)
+ name = nij.ij.object().getName(view)
self.viewer.add_image(data=py_image, name=name)
else:
raise ValueError(f"{view} cannot be displayed in napari!")
@@ -255,6 +266,36 @@ def disable_popup(self):
)
+class REPLButton(IJMenuButton):
+ def __init__(self, viewer: Viewer):
+ super().__init__(viewer)
+ self.viewer = viewer
+
+ self.setEnabled(False)
+
+ icon = QColoredSVGIcon(resource_path("repl"))
+ self.setIcon(icon.colored(theme=viewer.theme))
+
+ self.clicked.connect(self._toggle_repl)
+ self._widget = None
+
+ def _add_repl_to_dock(self):
+ self._widget = REPLWidget(self.repl)
+ self._widget.visible = False
+ self.viewer.window.add_dock_widget(self._widget, name="napari-imagej REPL")
+
+ def _toggle_repl(self):
+ """
+ Toggle visibility the SciJava REPL widget.
+ """
+ if not self._widget:
+ self._add_repl_to_dock()
+ else:
+ self.viewer.window.remove_dock_widget(self._widget)
+ self._widget.close()
+ self._widget = None
+
+
class SettingsButton(IJMenuButton):
# Signal used to identify changes to user settings
setting_change = Signal(bool)
diff --git a/src/napari_imagej/widgets/napari_imagej.py b/src/napari_imagej/widgets/napari_imagej.py
index 9ef004b9..6d0ae5f1 100644
--- a/src/napari_imagej/widgets/napari_imagej.py
+++ b/src/napari_imagej/widgets/napari_imagej.py
@@ -1,8 +1,8 @@
"""
-This module contains ImageJWidget, the top-level QWidget enabling
+This module contains NapariImageJWidget, the top-level QWidget enabling
graphical access to ImageJ functionality.
-This Widget is made accessible to napari through napari.yml
+This widget is made accessible to napari through napari.yml.
"""
from traceback import format_exception
from typing import Callable
@@ -15,7 +15,8 @@
from qtpy.QtWidgets import QTreeWidgetItem, QVBoxLayout, QWidget
from scyjava import jstacktrace, when_jvm_stops
-from napari_imagej.java import ij, init_ij, jc
+from napari_imagej import nij
+from napari_imagej.java import init_ij, jc
from napari_imagej.utilities._module_utils import _non_layer_widget
from napari_imagej.utilities.event_subscribers import (
NapariEventSubscriber,
@@ -183,7 +184,7 @@ def _update_progress(self, event: "jc.ModuleEvent"):
):
pm.close(module)
if isinstance(event, jc.ModuleErroredEvent):
- if not ij().ui().isVisible():
+ if not nij.ij.ui().isVisible():
# TODO Use napari's error handler once it works better
# see https://github.com/imagej/napari-imagej/issues/234
module_title = str(module.getInfo().getTitle())
@@ -233,8 +234,8 @@ def run(self):
Functionality partitioned into functions by subwidget.
"""
try:
- # Initialize ImageJ
- init_ij()
+ # Block until ImageJ2 is initialized.
+ ij = nij.ij
# Finalize the menu
self.widget.menu.finalize()
# Finalize the search bar
@@ -271,19 +272,19 @@ def searchCompleted(self, event: "jc.SearchEvent"):
[NapariImageJSearchListener(self.widget.result_tree.process)]
)
self.widget.result_tree._searchOperation = (
- ij().get("org.scijava.search.SearchService").search(listener_arr)
+ nij.ij.get("org.scijava.search.SearchService").search(listener_arr)
)
# Make sure that the search stops when we close napari
# Otherwise the Java threads like to continue
when_jvm_stops(self.widget.result_tree._searchOperation.terminate)
# Add SearcherTreeItems for each Searcher
- searchers = ij().plugin().createInstancesOfType(jc.Searcher)
+ searchers = nij.ij.plugin().createInstancesOfType(jc.Searcher)
for searcher in searchers:
self.widget.result_tree.insert.emit(
SearcherTreeItem(
searcher,
- checked=ij()
+ checked=nij.ij
.get("org.scijava.search.SearchService")
.enabled(searcher),
expanded=False,
@@ -291,20 +292,20 @@ def searchCompleted(self, event: "jc.SearchEvent"):
)
def _finalize_info_bar(self):
- self.widget.info_box.version_bar.setText(f"ImageJ2 v{ij().getVersion()}")
+ self.widget.info_box.version_bar.setText(f"ImageJ2 v{nij.ij.getVersion()}")
def _finalize_subscribers(self):
# Progress bar subscriber
self.progress_listener = ProgressBarListener(self.widget.progress_handler)
- subscribe(ij(), self.progress_listener)
+ subscribe(nij.ij, self.progress_listener)
# Debug printer subscriber
if is_debug():
self.event_listener = NapariEventSubscriber()
- subscribe(ij(), self.event_listener)
+ subscribe(nij.ij, self.event_listener)
def _clean_subscribers(self):
# Unsubscribe listeners
if hasattr(self, "progress_listener"):
- unsubscribe(ij(), self.progress_listener)
+ unsubscribe(nij.ij, self.progress_listener)
if hasattr(self, "event_listener"):
- unsubscribe(ij(), self.event_listener)
+ unsubscribe(nij.ij, self.event_listener)
diff --git a/src/napari_imagej/widgets/repl.py b/src/napari_imagej/widgets/repl.py
new file mode 100644
index 00000000..35cce06e
--- /dev/null
+++ b/src/napari_imagej/widgets/repl.py
@@ -0,0 +1,72 @@
+"""
+A widget that provides access to the SciJava REPL.
+
+This supports all of the languages of SciJava.
+"""
+from qtpy.QtGui import QTextCursor
+from qtpy.QtWidgets import QComboBox, QLineEdit, QTextEdit, QVBoxLayout, QWidget
+
+from napari_imagej.model import NapariImageJ
+
+
+class REPLWidget(QWidget):
+ def __init__(self, nij: NapariImageJ, parent: QWidget = None):
+ """
+ Initialize the REPLWidget.
+
+ :param nij: The NapariImageJ model object to use when evaluating commands.
+ :param parent: The parent widget (optional).
+ """
+ super().__init__(parent)
+
+ self.script_repl = nij.repl
+
+ layout = QVBoxLayout(self)
+
+ self.language_combo = QComboBox(self)
+ self.language_combo.addItems(
+ [str(el) for el in list(self.script_repl.getInterpretedLanguages())]
+ )
+ self.language_combo.currentTextChanged.connect(self.change_language)
+ layout.addWidget(self.language_combo)
+
+ self.output_textedit = QTextEdit(self)
+ self.output_textedit.setReadOnly(True)
+ layout.addWidget(self.output_textedit)
+
+ nij.add_repl_callback(lambda s: self.process_output(s))
+
+ self.input_lineedit = QLineEdit(self)
+ self.input_lineedit.returnPressed.connect(self.process_input)
+ layout.addWidget(self.input_lineedit)
+
+ def change_language(self, language: str):
+ """
+ Change the active scripting language of the REPL.
+
+ :param language: The new scripting language to use.
+ """
+ self.script_repl.lang(language)
+ self.output_textedit.clear()
+
+ def process_input(self):
+ """
+ Process the user input and evaluate it using the REPL.
+ """
+ input_text = self.input_lineedit.text()
+ self.input_lineedit.clear()
+
+ # Evaluate the input using REPL's evaluate method.
+ self.script_repl.evaluate(input_text)
+
+ def process_output(self, s):
+ """
+ Display output given from the REPL in the output text area.
+ """
+ self.output_textedit.append(s)
+
+ # Scroll to the bottom of the output text area.
+ cursor = self.output_textedit.textCursor()
+ cursor.movePosition(QTextCursor.MoveOperation.End)
+ self.output_textedit.setTextCursor(cursor)
+ self.output_textedit.ensureCursorVisible()
diff --git a/src/napari_imagej/widgets/result_tree.py b/src/napari_imagej/widgets/result_tree.py
index 4a35a91c..2d6737b6 100644
--- a/src/napari_imagej/widgets/result_tree.py
+++ b/src/napari_imagej/widgets/result_tree.py
@@ -9,7 +9,8 @@
from qtpy.QtWidgets import QAction, QMenu, QTreeWidget, QTreeWidgetItem
from scyjava import Priority
-from napari_imagej.java import ij, jc
+from napari_imagej import nij
+from napari_imagej.java import jc
from napari_imagej.utilities.logging import log_debug
from napari_imagej.widgets.widget_utils import python_actions_for
@@ -53,11 +54,11 @@ def __init__(
:param expanded: Indicates whether this SearcherTreeItem should start expanded
"""
super().__init__()
- self.title = ij().py.from_java(searcher.title())
+ self.title = nij.ij.py.from_java(searcher.title())
self._searcher = searcher
# Finding the priority is tricky - Searchers don't know their priority
# To find it we have to ask the pluginService.
- plugin_info = ij().plugin().getPlugin(searcher.getClass())
+ plugin_info = nij.ij.plugin().getPlugin(searcher.getClass())
self.priority = plugin_info.getPriority() if plugin_info else priority
# Set QtPy properties
@@ -202,14 +203,14 @@ def _register_item_change(self, item: QTreeWidgetItem, column: int):
if column == 0:
if isinstance(item, SearcherTreeItem):
checked = item.checkState(0) == Qt.Checked
- ij().get("org.scijava.search.SearchService").setEnabled(
+ nij.ij.get("org.scijava.search.SearchService").setEnabled(
item._searcher, checked
)
if not checked:
item.update([])
def _get_matching_item(self, searcher: "jc.Searcher") -> Optional[SearcherTreeItem]:
- name: str = ij().py.from_java(searcher.title())
+ name: str = nij.ij.py.from_java(searcher.title())
matches = self.findItems(name, Qt.MatchStartsWith, 0)
if len(matches) == 0:
return None
diff --git a/src/napari_imagej/widgets/widget_utils.py b/src/napari_imagej/widgets/widget_utils.py
index 578d829b..07948b28 100644
--- a/src/napari_imagej/widgets/widget_utils.py
+++ b/src/napari_imagej/widgets/widget_utils.py
@@ -20,7 +20,8 @@
QWidget,
)
-from napari_imagej.java import ij, jc
+from napari_imagej import nij
+from napari_imagej.java import jc
from napari_imagej.utilities._module_utils import (
execute_function_modally,
functionify_module_execution,
@@ -34,7 +35,7 @@ def python_actions_for(
):
actions = []
# Iterate over all available python actions
- searchService = ij().get("org.scijava.search.SearchService")
+ searchService = nij.ij.get("org.scijava.search.SearchService")
for action in searchService.actions(result):
action_name = str(action.toString())
# Add buttons for the java action
@@ -59,8 +60,8 @@ def execute_result(modal: bool):
return []
if (
- ij().legacy
- and ij().legacy.isActive()
+ nij.ij.legacy
+ and nij.ij.legacy.isActive()
and isinstance(moduleInfo, jc.LegacyCommandInfo)
):
reply = QMessageBox.question(
@@ -74,10 +75,10 @@ def execute_result(modal: bool):
QMessageBox.Yes | QMessageBox.No,
)
if reply == QMessageBox.Yes:
- ij().thread().queue(lambda: ij().ui().showUI())
+ nij.ij.thread().queue(lambda: nij.ij.ui().showUI())
return
- module = ij().module().createModule(moduleInfo)
+ module = nij.ij.module().createModule(moduleInfo)
# preprocess using napari GUI
func, param_options = functionify_module_execution(
@@ -281,12 +282,12 @@ def pass_to_ij():
img = self.img_container.combo.currentData()
roi = self.roi_container.combo.currentData()
# Convert the selections to Java equivalents
- j_img = ij().py.to_java(
+ j_img = nij.ij.py.to_java(
img, dim_order=self.dims_container.provided_labels()
)
if roi:
- j_img.getProperties().put("rois", ij().py.to_java(roi))
+ j_img.getProperties().put("rois", nij.ij.py.to_java(roi))
# Show the resulting image
- ij().ui().show(j_img)
+ nij.ij.ui().show(j_img)
- ij().thread().queue(lambda: pass_to_ij())
+ nij.ij.thread().queue(lambda: pass_to_ij())
diff --git a/tests/test_scripting.py b/tests/test_scripting.py
index 42183af6..f0dd0924 100644
--- a/tests/test_scripting.py
+++ b/tests/test_scripting.py
@@ -3,7 +3,7 @@
"""
from magicgui import magicgui
-from napari_imagej.java import JavaClasses
+from scyjava import JavaClasses
from napari_imagej.utilities._module_utils import functionify_module_execution
@@ -12,7 +12,7 @@ class JavaClassesTest(JavaClasses):
Here we override JavaClasses to get extra test imports
"""
- @JavaClasses.blocking_import
+ @JavaClasses.java_import
def DefaultModuleService(self):
return "org.scijava.module.DefaultModuleService"
diff --git a/tests/types/test_trackmate.py b/tests/types/test_trackmate.py
index 9cb014bf..52b953be 100644
--- a/tests/types/test_trackmate.py
+++ b/tests/types/test_trackmate.py
@@ -5,23 +5,23 @@
import numpy as np
import pytest
+from scyjava import JavaClasses
from napari.layers import Labels, Tracks
from napari_imagej import settings
-from napari_imagej.java import JavaClasses
from napari_imagej.types.converters.trackmate import TrackMateClasses, trackmate_present
class TestTrackMateClasses(TrackMateClasses):
- @JavaClasses.blocking_import
+ @JavaClasses.java_import
def DisplaySettings(self):
return "fiji.plugin.trackmate.gui.displaysettings.DisplaySettings"
- @JavaClasses.blocking_import
+ @JavaClasses.java_import
def HyperStackDisplayer(self):
return "fiji.plugin.trackmate.visualization.hyperstack.HyperStackDisplayer"
- @JavaClasses.blocking_import
+ @JavaClasses.java_import
def SelectionModel(self):
return "fiji.plugin.trackmate.SelectionModel"
diff --git a/tests/utils.py b/tests/utils.py
index eda8f349..e9a80c99 100644
--- a/tests/utils.py
+++ b/tests/utils.py
@@ -5,7 +5,7 @@
from jpype import JImplements, JOverride
-from napari_imagej.java import JavaClasses
+from scyjava import JavaClasses
class JavaClassesTest(JavaClasses):
@@ -13,115 +13,115 @@ class JavaClassesTest(JavaClasses):
Here we override JavaClasses to get extra test imports
"""
- @JavaClasses.blocking_import
+ @JavaClasses.java_import
def ArrayImg(self):
return "net.imglib2.img.array.ArrayImg"
- @JavaClasses.blocking_import
+ @JavaClasses.java_import
def ArrayImgs(self):
return "net.imglib2.img.array.ArrayImgs"
- @JavaClasses.blocking_import
+ @JavaClasses.java_import
def Axes(self):
return "net.imagej.axis.Axes"
- @JavaClasses.blocking_import
+ @JavaClasses.java_import
def BoolType(self):
return "net.imglib2.type.logic.BoolType"
- @JavaClasses.blocking_import
+ @JavaClasses.java_import
def ByteType(self):
return "net.imglib2.type.numeric.integer.ByteType"
- @JavaClasses.blocking_import
+ @JavaClasses.java_import
def ClassesSearcher(self):
return "org.scijava.search.classes.ClassesSearcher"
- @JavaClasses.blocking_import
+ @JavaClasses.java_import
def ClassSearchResult(self):
return "org.scijava.search.classes.ClassSearchResult"
- @JavaClasses.blocking_import
+ @JavaClasses.java_import
def DefaultMutableModuleItem(self):
return "org.scijava.module.DefaultMutableModuleItem"
- @JavaClasses.blocking_import
+ @JavaClasses.java_import
def DefaultMutableModuleInfo(self):
return "org.scijava.module.DefaultMutableModuleInfo"
- @JavaClasses.blocking_import
+ @JavaClasses.java_import
def DoubleArray(self):
return "org.scijava.util.DoubleArray"
- @JavaClasses.blocking_import
+ @JavaClasses.java_import
def EuclideanSpace(self):
return "net.imglib2.EuclideanSpace"
- @JavaClasses.blocking_import
+ @JavaClasses.java_import
def FloatType(self):
return "net.imglib2.type.numeric.real.FloatType"
- @JavaClasses.blocking_import
+ @JavaClasses.java_import
def Frame(self):
return "java.awt.Frame"
- @JavaClasses.blocking_import
+ @JavaClasses.java_import
def IllegalArgumentException(self):
return "java.lang.IllegalArgumentException"
- @JavaClasses.blocking_import
+ @JavaClasses.java_import
def ImageDisplay(self):
return "net.imagej.display.ImageDisplay"
- @JavaClasses.blocking_import
+ @JavaClasses.java_import
def IntType(self):
return "net.imglib2.type.numeric.integer.IntType"
- @JavaClasses.blocking_import
+ @JavaClasses.java_import
def ItemIO(self):
return "org.scijava.ItemIO"
- @JavaClasses.blocking_import
+ @JavaClasses.java_import
def ItemVisibility(self):
return "org.scijava.ItemVisibility"
- @JavaClasses.blocking_import
+ @JavaClasses.java_import
def ModuleSearchResult(self):
return "org.scijava.search.module.ModuleSearchResult"
- @JavaClasses.blocking_import
+ @JavaClasses.java_import
def OpSearchResult(self):
return "net.imagej.ops.search.OpSearchResult"
- @JavaClasses.blocking_import
+ @JavaClasses.java_import
def ScriptInfo(self):
return "org.scijava.script.ScriptInfo"
- @JavaClasses.blocking_import
+ @JavaClasses.java_import
def ShortType(self):
return "net.imglib2.type.numeric.integer.ShortType"
- @JavaClasses.blocking_import
+ @JavaClasses.java_import
def System(self):
return "java.lang.System"
- @JavaClasses.blocking_import
+ @JavaClasses.java_import
def UnsignedByteType(self):
return "net.imglib2.type.numeric.integer.UnsignedByteType"
- @JavaClasses.blocking_import
+ @JavaClasses.java_import
def UnsignedShortType(self):
return "net.imglib2.type.numeric.integer.UnsignedShortType"
- @JavaClasses.blocking_import
+ @JavaClasses.java_import
def UnsignedIntType(self):
return "net.imglib2.type.numeric.integer.UnsignedIntType"
- @JavaClasses.blocking_import
+ @JavaClasses.java_import
def UnsignedLongType(self):
return "net.imglib2.type.numeric.integer.UnsignedLongType"
- @JavaClasses.blocking_import
+ @JavaClasses.java_import
def WindowEvent(self):
return "java.awt.event.WindowEvent"
diff --git a/tests/widgets/test_napari_imagej.py b/tests/widgets/test_napari_imagej.py
index b6fe770a..35ef8f04 100644
--- a/tests/widgets/test_napari_imagej.py
+++ b/tests/widgets/test_napari_imagej.py
@@ -8,7 +8,7 @@
from qtpy.QtCore import Qt
from qtpy.QtWidgets import QApplication, QLabel, QPushButton, QTextEdit, QVBoxLayout
-from napari_imagej.java import ij
+from napari_imagej import nij
from napari_imagej.utilities.event_subscribers import (
ProgressBarListener,
UIShownListener,
@@ -56,7 +56,7 @@ def _run_buttons(imagej_widget: NapariImageJWidget):
def _ensure_searchers_available(imagej_widget: NapariImageJWidget, asserter):
tree = imagej_widget.result_tree
# Find the ModuleSearcher
- numSearchers = len(ij().plugin().getPluginsOfType(jc.Searcher))
+ numSearchers = len(nij.ij.plugin().getPluginsOfType(jc.Searcher))
try:
asserter(lambda: tree.topLevelItemCount() == numSearchers)
except Exception:
@@ -170,7 +170,7 @@ def test_imagej_search_tree_disable(ij, imagej_widget: NapariImageJWidget, asser
# Disable the searcher, assert the proper ImageJ response
searcher_item.setCheckState(0, Qt.Unchecked)
asserter(
- lambda: not ij.get("org.scijava.search.SearchService").enabled(
+ lambda: not nij.ij.get("org.scijava.search.SearchService").enabled(
searcher_item._searcher
)
)
@@ -178,7 +178,7 @@ def test_imagej_search_tree_disable(ij, imagej_widget: NapariImageJWidget, asser
# Enabled the searcher, assert the proper ImageJ response
searcher_item.setCheckState(0, Qt.Checked)
asserter(
- lambda: ij.get("org.scijava.search.SearchService").enabled(
+ lambda: nij.ij.get("org.scijava.search.SearchService").enabled(
searcher_item._searcher
)
)
@@ -190,7 +190,7 @@ def test_widget_finalization(ij, imagej_widget: NapariImageJWidget, asserter):
# Ensure that all Searchers are represented in the tree with a top level
# item
- numSearchers = len(ij.plugin().getPluginsOfType(jc.Searcher))
+ numSearchers = len(nij.ij.plugin().getPluginsOfType(jc.Searcher))
asserter(lambda: imagej_widget.result_tree.topLevelItemCount() == numSearchers)
@@ -232,12 +232,12 @@ def test_info_validity(imagej_widget: NapariImageJWidget, qtbot, asserter):
"""
# Wait for the info to populate
- ij()
+ ij = nij.ij
# Check the version
info_box = imagej_widget.info_box
- asserter(lambda: info_box.version_bar.text() == f"ImageJ2 v{ij().getVersion()}")
+ asserter(lambda: info_box.version_bar.text() == f"ImageJ2 v{ij.getVersion()}")
def test_handle_output_layer(imagej_widget: NapariImageJWidget, qtbot, asserter):
diff --git a/tests/widgets/test_result_runner.py b/tests/widgets/test_result_runner.py
index bec93104..77c2af8a 100644
--- a/tests/widgets/test_result_runner.py
+++ b/tests/widgets/test_result_runner.py
@@ -4,13 +4,13 @@
import pytest
from qtpy.QtWidgets import QLabel, QVBoxLayout, QWidget
-from napari_imagej.java import JavaClasses
+from scyjava import JavaClasses
from napari_imagej.widgets.layouts import QFlowLayout
from napari_imagej.widgets.result_runner import ResultRunner
class JavaClassesTest(JavaClasses):
- @JavaClasses.blocking_import
+ @JavaClasses.java_import
def ModuleSearchResult(self):
return "org.scijava.search.module.ModuleSearchResult"