From f8e6cd2f0dd68b8d7d7851f814aaec83c8873a8b Mon Sep 17 00:00:00 2001 From: Curtis Rueden Date: Wed, 16 Nov 2022 13:23:23 -0600 Subject: [PATCH 01/12] Always queue ui.show/showUI calls on the EDT This avoids threading issues which cause deadlocks on macOS. --- src/napari_imagej/widgets/menu.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/napari_imagej/widgets/menu.py b/src/napari_imagej/widgets/menu.py index 62fd968f..dfbb4a22 100644 --- a/src/napari_imagej/widgets/menu.py +++ b/src/napari_imagej/widgets/menu.py @@ -62,7 +62,7 @@ def _showUI(self): # First time showing if not self.gui.isVisible(): # First things first, show the GUI - ij().ui().showUI(self.gui) + ij().thread().queue(lambda: ij().ui().showUI(self.gui)) # Then, add our custom settings to the User Interface if ij().legacy and ij().legacy.isActive(): self._ij1_UI_setup() @@ -70,7 +70,7 @@ def _showUI(self): self._ij2_UI_setup() # Later shows - the GUI is "visible", but the appFrame probably isn't else: - self.gui.getApplicationFrame().setVisible(True) + ij().thread().queue(lambda: self.gui.getApplicationFrame().setVisible(True)) def _ij1_UI_setup(self): """Configures the ImageJ Legacy GUI""" @@ -148,7 +148,7 @@ def _set_icon(self, path: str): def send_active_layer(self): active_layer: Optional[Layer] = self.viewer.layers.selection.active if active_layer: - ij().ui().show(ij().py.to_java(active_layer)) + self._show(active_layer) else: log_debug("There is no active layer to export to ImageJ2") @@ -165,7 +165,10 @@ def send_chosen_layer(self): layer = choices["layer"] if isinstance(layer, Layer): # Pass the relevant data to ImageJ2 - ij().ui().show(ij().py.to_java(layer)) + self._show(layer) + + def _show(self, layer): + ij().thread().queue(lambda: ij().ui().show(ij().py.to_java(layer))) class FromIJButton(QPushButton): From 907d916b2c822d71c74c064146591ccf53718ebc Mon Sep 17 00:00:00 2001 From: Curtis Rueden Date: Wed, 16 Nov 2022 13:35:50 -0600 Subject: [PATCH 02/12] Fix usage of private and now-removed PyImageJ API --- src/napari_imagej/types/converters/images.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/napari_imagej/types/converters/images.py b/src/napari_imagej/types/converters/images.py index 62d4554e..a45185a5 100644 --- a/src/napari_imagej/types/converters/images.py +++ b/src/napari_imagej/types/converters/images.py @@ -58,7 +58,7 @@ def _dataset_to_image(image: Any) -> Image: def _image_to_dataset(image: Image) -> "jc.Dataset": # Construct a dataset from the data data = image.data - dataset: "jc.Dataset" = ij().py._numpy_to_dataset(data) + dataset: "jc.Dataset" = ij().py.to_dataset(data) # Add name dataset.setName(image.name) # Add color table, if the image uses a custom colormap From b6ec9c69ae1fed1dabe2b1a6df6514bf2bfc2008 Mon Sep 17 00:00:00 2001 From: Curtis Rueden Date: Wed, 16 Nov 2022 13:37:55 -0600 Subject: [PATCH 03/12] Use existing imagej.gateway if already running In a future PyImageJ release, when running in GUI mode (and *only* when running in GUI mode), the created ImageJ2 gateway will be cached in the global variable imagej.gateway, so that it is accessible from other threads, since the imagej.init function never returns in that case. We can utilize this in napari-imagej to hook into that ImageJ2 gateway, rather than creating a new one. By doing this, napari-imagej becomes usable interactively while PyImageJ is in GUI mode, which means it can now work with a GUI on macOS! But you need to start napari + PyImageJ in a special way: import imagej, napari napari.Viewer() imagej.init(mode='gui') As an aside: this construction does *not* work on non-macOS platforms, because imagej.init(mode='gui') manually blocks the thread with a polling sleep loop, which means that napari.run() does not get invoked to start up napari's event loop, which results in napari hanging. However, on Linux (and likely Windows) you can instead do: import imagej, napari imagej.gateway = imagej.init(mode='interactive') imagej.gateway.ui().showUI() napari.Viewer() napari.run() To utilize the pathway enabled by this commit. --- src/napari_imagej/java.py | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/src/napari_imagej/java.py b/src/napari_imagej/java.py index aa1f7de7..6cf383bb 100644 --- a/src/napari_imagej/java.py +++ b/src/napari_imagej/java.py @@ -107,8 +107,16 @@ def initialize(self): ij_settings["ij_dir_or_version_or_endpoint"] = settings[ "imagej_directory_or_endpoint" ].get(str) - ij_settings["mode"] = settings["jvm_mode"].get(str) - ij_settings["add_legacy"] = settings["include_imagej_legacy"].get(bool) + if hasattr(imagej, "gateway") and imagej.gateway: + ij_settings["mode"] = ( + "headless" if imagej.gateway.ui().isHeadless() else "gui" + ) + ij_settings["add_legacy"] = ( + imagej.gateway.legacy and imagej.gateway.legacy.isActive() + ) + else: + ij_settings["mode"] = settings["jvm_mode"].get(str) + ij_settings["add_legacy"] = settings["include_imagej_legacy"].get(bool) # napari-imagej configuration from napari_imagej.types.converters import install_converters @@ -118,7 +126,11 @@ def initialize(self): log_debug("Completed JVM Configuration") # Launch PyImageJ - self.ij = imagej.init(**ij_settings) + self.ij = ( + imagej.gateway + if hasattr(imagej, "gateway") and imagej.gateway + else imagej.init(**ij_settings) + ) # Validate PyImageJ self._validate_imagej() # Log initialization From 1e88ec29423c832f0a4fcda4ec687fbcabc542dc Mon Sep 17 00:00:00 2001 From: Curtis Rueden Date: Wed, 16 Nov 2022 21:54:41 -0600 Subject: [PATCH 04/12] Add a hack to avoid FlatLaf in problem situations Specifically, if the UI is swing rather than legacy, then FlatLaf doesn't work with napari-imagej for some reason. We need to figure out why, but for the moment, this HACK avoids the issue by switching to the system default Look+Feel proactively before the Java UI can crash. Unfortunately, it has the side effect of changing the user's preferred Look+Feel preference globally for all their ImageJ2 instances, not just within napari-imagej. So we'll eventually need something better. And in any case, we should make FlatLaf work, since FlatLaf is great. --- src/napari_imagej/java.py | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/napari_imagej/java.py b/src/napari_imagej/java.py index 6cf383bb..ec4e1d61 100644 --- a/src/napari_imagej/java.py +++ b/src/napari_imagej/java.py @@ -133,6 +133,26 @@ def initialize(self): ) # Validate PyImageJ self._validate_imagej() + + # HACK: Avoid FlatLaf with ImageJ2 Swing UI; it doesn't work for reasons unknown. + try: + ui = self.ij.ui().getDefaultUI().getInfo().getName() + log_debug(f"Default SciJava UI is {ui}.") + if ui == "swing": + SwingLookAndFeelService = jimport("org.scijava.ui.swing.laf.SwingLookAndFeelService") + laf = self.ij.prefs().get(SwingLookAndFeelService, "lookAndFeel") + log_debug(f"Preferred Look+Feel is {laf}.") + if laf is None or laf.startsWith("FlatLaf"): + UIManager = jimport("javax.swing.UIManager") + fallback_laf = UIManager.getSystemLookAndFeelClassName() + log_debug(f"Detected FlatLaf. Falling back to {fallback_laf} instead to avoid problems.") + self.ij.prefs().put(SwingLookAndFeelService, "lookAndFeel", fallback_laf) + except Exception as exc: + from scyjava import jstacktrace + print(jstacktrace(exc)) + # NB: The hack failed, but no worries, just try to keep going. + pass + # Log initialization log_debug(f"Initialized at version {self.ij.getVersion()}") From 8baab60ea1705b921397a4f11766a1e1ae178d55 Mon Sep 17 00:00:00 2001 From: Curtis Rueden Date: Thu, 17 Nov 2022 10:13:55 -0600 Subject: [PATCH 05/12] Include imagej-legacy by default This avoids the issue with FlatLaf not working with the Swing UI. But we'll need to figure out what to do about imagej/imagej-legacy#280. --- src/napari_imagej/config_default.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/napari_imagej/config_default.yaml b/src/napari_imagej/config_default.yaml index 7cd50f2f..53e690c2 100644 --- a/src/napari_imagej/config_default.yaml +++ b/src/napari_imagej/config_default.yaml @@ -19,7 +19,7 @@ imagej_base_directory: '.' # This can be used to include original ImageJ functionality. # Iff true, original ImageJ functionality (ij.* packages) will be available. # Defaults to false. -include_imagej_legacy: false +include_imagej_legacy: true # Designates the mode of execution for ImageJ2. # Allowed options are 'headless' and 'interactive'. From 4b74cc0d9c3acb7ee709f4e0a108d61f38c27115 Mon Sep 17 00:00:00 2001 From: Gabriel Selzer Date: Thu, 17 Nov 2022 12:57:19 -0600 Subject: [PATCH 06/12] Update imagej API We have to bump the minimum pyimagej version to use it --- dev-environment.yml | 2 +- environment.yml | 2 +- setup.cfg | 2 +- src/napari_imagej/types/converters/images.py | 2 +- src/napari_imagej/types/converters/labels.py | 3 ++- src/napari_imagej/utilities/_module_utils.py | 3 ++- 6 files changed, 8 insertions(+), 6 deletions(-) diff --git a/dev-environment.yml b/dev-environment.yml index be972466..4080ebf5 100644 --- a/dev-environment.yml +++ b/dev-environment.yml @@ -25,7 +25,7 @@ dependencies: - magicgui >= 0.5.1 - napari - openjdk=8 - - pyimagej >= 1.2.0 + - pyimagej >= 1.3.0 - scyjava >= 1.7.0 # Test dependencies - numpy diff --git a/environment.yml b/environment.yml index b27f6575..40f0a276 100644 --- a/environment.yml +++ b/environment.yml @@ -26,7 +26,7 @@ dependencies: - napari - numpy - openjdk=8 - - pyimagej >= 1.2.0 + - pyimagej >= 1.3.0 - scyjava >= 1.7.0 # Project from source - pip diff --git a/setup.cfg b/setup.cfg index 0142c576..e06d3195 100644 --- a/setup.cfg +++ b/setup.cfg @@ -44,7 +44,7 @@ install_requires = napari numpy magicgui >= 0.5.1 - pyimagej >= 1.2.0 + pyimagej >= 1.3.0 scyjava >= 1.7.0 [options.packages.find] diff --git a/src/napari_imagej/types/converters/images.py b/src/napari_imagej/types/converters/images.py index a45185a5..53a34c5a 100644 --- a/src/napari_imagej/types/converters/images.py +++ b/src/napari_imagej/types/converters/images.py @@ -27,7 +27,7 @@ def _dataset_view_to_image(image: Any) -> Image: data=ij().py.from_java(view.getData().getImgPlus().getImg()), name=view.getData().getName(), ) - if view.getColorTables().size() > 0: + if view.getColorTables() and view.getColorTables().size() > 0: if not jc.ColorTables.isGrayColorTable(view.getColorTables().get(0)): kwargs["colormap"] = _color_table_to_colormap(view.getColorTables().get(0)) return Image(**kwargs) diff --git a/src/napari_imagej/types/converters/labels.py b/src/napari_imagej/types/converters/labels.py index a484a5c5..6a8d03e3 100644 --- a/src/napari_imagej/types/converters/labels.py +++ b/src/napari_imagej/types/converters/labels.py @@ -2,6 +2,7 @@ scyjava Converters for converting between ImgLib2 ImgLabelings and napari Labels """ +from imagej.convert import imglabeling_to_labeling from labeling.Labeling import Labeling from napari.layers import Labels from scyjava import Priority @@ -39,7 +40,7 @@ def _imglabeling_to_layer(imgLabeling: "jc.ImgLabeling") -> Labels: :param imgLabeling: the Java ImgLabeling :return: a Labels layer """ - labeling: Labeling = ij().py._imglabeling_to_labeling(imgLabeling) + labeling: Labeling = imglabeling_to_labeling(ij(), imgLabeling) return _labeling_to_layer(labeling) diff --git a/src/napari_imagej/utilities/_module_utils.py b/src/napari_imagej/utilities/_module_utils.py index 5eafa552..33f7b254 100644 --- a/src/napari_imagej/utilities/_module_utils.py +++ b/src/napari_imagej/utilities/_module_utils.py @@ -13,6 +13,7 @@ from inspect import Parameter, Signature, _empty, isclass, signature from typing import Any, Callable, Dict, List, Optional, Tuple, Union +from imagej.images import is_arraylike from jpype import JException from magicgui.widgets import Container, Label, LineEdit, Widget, request_values from magicgui.widgets._bases import CategoricalWidget @@ -306,7 +307,7 @@ def _pure_module_outputs( continue output = ij().py.from_java(output_entry.getValue()) # Add arraylike outputs as images - if ij().py._is_arraylike(output): + if is_arraylike(output): layer = Layer.create(data=output, meta={"name": name}, layer_type="image") layer_outputs.append(layer) # Add Layers directly From 008daecd19bd2330ef60eb69932a52ed7fadd103 Mon Sep 17 00:00:00 2001 From: Gabriel Selzer Date: Thu, 17 Nov 2022 13:43:51 -0600 Subject: [PATCH 07/12] Run post-showing setup on Java GUI thread, too Otherwise the post-showing setup may not work as intended. For example, closing the IJ2 GUI will terminate napari, if we don't do this. --- src/napari_imagej/widgets/menu.py | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/src/napari_imagej/widgets/menu.py b/src/napari_imagej/widgets/menu.py index dfbb4a22..cd4d9aa3 100644 --- a/src/napari_imagej/widgets/menu.py +++ b/src/napari_imagej/widgets/menu.py @@ -61,13 +61,18 @@ def _showUI(self): """ # First time showing if not self.gui.isVisible(): - # First things first, show the GUI - ij().thread().queue(lambda: ij().ui().showUI(self.gui)) - # Then, add our custom settings to the User Interface - if ij().legacy and ij().legacy.isActive(): - self._ij1_UI_setup() - else: - self._ij2_UI_setup() + + def ui_setup(): + # First things first, show the GUI + ij().ui().showUI(self.gui) + # Then, add our custom settings to the User Interface + if ij().legacy and ij().legacy.isActive(): + self._ij1_UI_setup() + else: + self._ij2_UI_setup() + + # Run the setup on the Java GUI Thread + ij().thread().queue(ui_setup) # Later shows - the GUI is "visible", but the appFrame probably isn't else: ij().thread().queue(lambda: self.gui.getApplicationFrame().setVisible(True)) From 2f348afc8ecd9e4326572b9ac54a26dcb3afbe7d Mon Sep 17 00:00:00 2001 From: Gabriel Selzer Date: Thu, 17 Nov 2022 13:55:42 -0600 Subject: [PATCH 08/12] Format code --- src/napari_imagej/java.py | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/src/napari_imagej/java.py b/src/napari_imagej/java.py index ec4e1d61..9bdfd2e0 100644 --- a/src/napari_imagej/java.py +++ b/src/napari_imagej/java.py @@ -134,24 +134,32 @@ def initialize(self): # Validate PyImageJ self._validate_imagej() - # HACK: Avoid FlatLaf with ImageJ2 Swing UI; it doesn't work for reasons unknown. + # HACK: Avoid FlatLaf with ImageJ2 Swing UI; + # it doesn't work for reasons unknown. try: ui = self.ij.ui().getDefaultUI().getInfo().getName() log_debug(f"Default SciJava UI is {ui}.") if ui == "swing": - SwingLookAndFeelService = jimport("org.scijava.ui.swing.laf.SwingLookAndFeelService") + SwingLookAndFeelService = jimport( + "org.scijava.ui.swing.laf.SwingLookAndFeelService" + ) laf = self.ij.prefs().get(SwingLookAndFeelService, "lookAndFeel") log_debug(f"Preferred Look+Feel is {laf}.") if laf is None or laf.startsWith("FlatLaf"): UIManager = jimport("javax.swing.UIManager") fallback_laf = UIManager.getSystemLookAndFeelClassName() - log_debug(f"Detected FlatLaf. Falling back to {fallback_laf} instead to avoid problems.") - self.ij.prefs().put(SwingLookAndFeelService, "lookAndFeel", fallback_laf) + log_debug( + f"Detected FlatLaf. Falling back to {fallback_laf} " + "instead to avoid problems." + ) + self.ij.prefs().put( + SwingLookAndFeelService, "lookAndFeel", fallback_laf + ) except Exception as exc: from scyjava import jstacktrace - print(jstacktrace(exc)) + # NB: The hack failed, but no worries, just try to keep going. - pass + print(jstacktrace(exc)) # Log initialization log_debug(f"Initialized at version {self.ij.getVersion()}") From 71c849abfec295ff75d4163fcf34b44ec23cee7a Mon Sep 17 00:00:00 2001 From: Gabriel Selzer Date: Thu, 17 Nov 2022 14:55:15 -0600 Subject: [PATCH 09/12] Partition ImageJ config and init into functions --- src/napari_imagej/java.py | 98 ++++++++++++++++++++++++++------------- 1 file changed, 65 insertions(+), 33 deletions(-) diff --git a/src/napari_imagej/java.py b/src/napari_imagej/java.py index 9bdfd2e0..6ea78c53 100644 --- a/src/napari_imagej/java.py +++ b/src/napari_imagej/java.py @@ -14,7 +14,7 @@ - object whose fields are lazily-loaded Java Class instances. """ from threading import Lock -from typing import Callable, List, Tuple +from typing import Any, Callable, Dict, List, Tuple import imagej from jpype import JClass @@ -89,53 +89,48 @@ def initialize(self): """ Creates the ImageJ instance """ - # Initialize ImageJ log_debug("Initializing ImageJ2") - # -- IMAGEJ CONFIG -- # + # determine whether imagej is already running + imagej_already_initialized: bool = hasattr(imagej, "gateway") and imagej.gateway - # ScyJava configuration - # TEMP: Avoid issues caused by - # https://github.com/imagej/pyimagej/issues/160 - config.add_repositories( - {"scijava.public": "https://maven.scijava.org/content/groups/public"} - ) - config.add_option(f"-Dimagej2.dir={settings['imagej_base_directory'].get(str)}") + # -- CONFIGURATION -- # - # PyImageJ configuration - ij_settings = {} - ij_settings["ij_dir_or_version_or_endpoint"] = settings[ - "imagej_directory_or_endpoint" - ].get(str) - if hasattr(imagej, "gateway") and imagej.gateway: - ij_settings["mode"] = ( - "headless" if imagej.gateway.ui().isHeadless() else "gui" - ) - ij_settings["add_legacy"] = ( - imagej.gateway.legacy and imagej.gateway.legacy.isActive() - ) + # Configure pyimagej + if imagej_already_initialized: + self._update_imagej_settings() else: - ij_settings["mode"] = settings["jvm_mode"].get(str) - ij_settings["add_legacy"] = settings["include_imagej_legacy"].get(bool) + ij_settings = self._configure_imagej() - # napari-imagej configuration + # Configure napari-imagej from napari_imagej.types.converters import install_converters install_converters() log_debug("Completed JVM Configuration") - # Launch PyImageJ - self.ij = ( - imagej.gateway - if hasattr(imagej, "gateway") and imagej.gateway - else imagej.init(**ij_settings) - ) + # -- INITIALIZATION -- # + + # Launch ImageJ + if imagej_already_initialized: + self.ij = imagej.gateway + else: + self.ij = imagej.init(**ij_settings) + + # Log initialization + log_debug(f"Initialized at version {self.ij.getVersion()}") + + # -- VALIDATION -- # + # Validate PyImageJ self._validate_imagej() # HACK: Avoid FlatLaf with ImageJ2 Swing UI; # it doesn't work for reasons unknown. + # NB this SHOULD NOT be moved. + # This code must be in place before ANY swing components get created. + # Swing components could be created by any Java functionality (e.g. Commands). + # Therefore, we can't move it to e.g. the menu file try: ui = self.ij.ui().getDefaultUI().getInfo().getName() log_debug(f"Default SciJava UI is {ui}.") @@ -161,8 +156,45 @@ def initialize(self): # NB: The hack failed, but no worries, just try to keep going. print(jstacktrace(exc)) - # Log initialization - log_debug(f"Initialized at version {self.ij.getVersion()}") + def _update_imagej_settings(self) -> None: + """ + Updates napari-imagej's settings to reflect an active ImageJ instance. + """ + # Scrape the JVM mode off of the active ImageJ instance + settings["jvm_mode"] = ( + "headless" if imagej.gateway.ui().isHeadless() else "interactive" + ) + # Determine if legacy is active on the active ImageJ instance + # NB bool is needed to coerce Nones into booleans. + settings["add_legacy"] = bool( + imagej.gateway.legacy and imagej.gateway.legacy.isActive() + ) + + def _configure_imagej(self) -> Dict[str, Any]: + """ + Configures scyjava and pyimagej. + This function returns the settings that must be passed in the + actual initialization call. + + :return: kwargs that should be passed to imagej.init() + """ + # ScyJava configuration + # TEMP: Avoid issues caused by + # https://github.com/imagej/pyimagej/issues/160 + config.add_repositories( + {"scijava.public": "https://maven.scijava.org/content/groups/public"} + ) + config.add_option(f"-Dimagej2.dir={settings['imagej_base_directory'].get(str)}") + + # PyImageJ configuration + init_settings = {} + init_settings["ij_dir_or_version_or_endpoint"] = settings[ + "imagej_directory_or_endpoint" + ].get(str) + init_settings["mode"] = settings["jvm_mode"].get(str) + init_settings["add_legacy"] = settings["include_imagej_legacy"].get(bool) + + return init_settings def _validate_imagej(self): """ From 0c813de7d1b2a397bbb63da6cc7be6f9da7f26b5 Mon Sep 17 00:00:00 2001 From: Gabriel Selzer Date: Thu, 17 Nov 2022 14:56:38 -0600 Subject: [PATCH 10/12] Add TODOs for scyjava EventQueue wrappers --- src/napari_imagej/widgets/menu.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/napari_imagej/widgets/menu.py b/src/napari_imagej/widgets/menu.py index cd4d9aa3..cfde5e18 100644 --- a/src/napari_imagej/widgets/menu.py +++ b/src/napari_imagej/widgets/menu.py @@ -72,9 +72,11 @@ def ui_setup(): self._ij2_UI_setup() # Run the setup on the Java GUI Thread + # TODO: Use EventQueue.invokeLater scyjava wrapper, once it exists ij().thread().queue(ui_setup) # Later shows - the GUI is "visible", but the appFrame probably isn't else: + # TODO: Use EventQueue.invokeLater scyjava wrapper, once it exists ij().thread().queue(lambda: self.gui.getApplicationFrame().setVisible(True)) def _ij1_UI_setup(self): @@ -173,6 +175,7 @@ def send_chosen_layer(self): self._show(layer) def _show(self, layer): + # TODO: Use EventQueue.invokeLater scyjava wrapper, once it exists ij().thread().queue(lambda: ij().ui().show(ij().py.to_java(layer))) From 251977f62246e0905e2d026a3b55e1e4cdfd372d Mon Sep 17 00:00:00 2001 From: Gabriel Selzer Date: Thu, 17 Nov 2022 14:58:26 -0600 Subject: [PATCH 11/12] Better comments for EDT queue calls --- src/napari_imagej/widgets/menu.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/napari_imagej/widgets/menu.py b/src/napari_imagej/widgets/menu.py index cfde5e18..5dcf95c9 100644 --- a/src/napari_imagej/widgets/menu.py +++ b/src/napari_imagej/widgets/menu.py @@ -71,11 +71,12 @@ def ui_setup(): else: self._ij2_UI_setup() - # Run the setup on the Java GUI Thread + # Queue UI call on the EDT # TODO: Use EventQueue.invokeLater scyjava wrapper, once it exists ij().thread().queue(ui_setup) # Later shows - the GUI is "visible", but the appFrame probably isn't else: + # Queue UI call on the EDT # TODO: Use EventQueue.invokeLater scyjava wrapper, once it exists ij().thread().queue(lambda: self.gui.getApplicationFrame().setVisible(True)) @@ -175,6 +176,7 @@ def send_chosen_layer(self): self._show(layer) def _show(self, 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))) From 9c0d65d942a5e6ab8fa6be671c5a6ced018f720e Mon Sep 17 00:00:00 2001 From: Gabriel Selzer Date: Fri, 18 Nov 2022 09:42:31 -0600 Subject: [PATCH 12/12] Configure legacy UI as the default --- .github/workflows/build.yml | 6 +++--- src/napari_imagej/config_default.yaml | 3 ++- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index fed7612c..c5e6a8ca 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -96,8 +96,8 @@ jobs: with: configuration: --check-only - test-imagej-legacy: - name: Test legacy inclusion + test-ij2: + name: Test pure IJ2 runs-on: ubuntu-latest defaults: # Steps that rely on the activated environment must be run with this shell setup. @@ -124,7 +124,7 @@ jobs: - name: Setup Testing Environment Variables run: | - echo "NAPARI_IMAGEJ_INCLUDE_IMAGEJ_LEGACY=true" >> $GITHUB_ENV + echo "NAPARI_IMAGEJ_INCLUDE_IMAGEJ_LEGACY=false" >> $GITHUB_ENV - name: Test napari-imagej uses: GabrielBB/xvfb-action@v1 diff --git a/src/napari_imagej/config_default.yaml b/src/napari_imagej/config_default.yaml index 53e690c2..80f5d093 100644 --- a/src/napari_imagej/config_default.yaml +++ b/src/napari_imagej/config_default.yaml @@ -18,7 +18,8 @@ imagej_base_directory: '.' # This can be used to include original ImageJ functionality. # Iff true, original ImageJ functionality (ij.* packages) will be available. -# Defaults to false. +# Iff false, many ImageJ2 rewrites of original ImageJ functionality are available. +# Defaults to true as the ImageJ legacy UI is most popular and familiar. include_imagej_legacy: true # Designates the mode of execution for ImageJ2.