diff --git a/resources/long_left_arrow.svg b/resources/long_left_arrow.svg
new file mode 100644
index 00000000..4bdd4bbb
--- /dev/null
+++ b/resources/long_left_arrow.svg
@@ -0,0 +1,7 @@
+
+
+
diff --git a/resources/long_right_arrow.svg b/resources/long_right_arrow.svg
new file mode 100644
index 00000000..47b2a880
--- /dev/null
+++ b/resources/long_right_arrow.svg
@@ -0,0 +1,7 @@
+
+
+
diff --git a/src/napari_imagej/widget_IJ2.py b/src/napari_imagej/widget_IJ2.py
index 486cc0f0..1445ca44 100644
--- a/src/napari_imagej/widget_IJ2.py
+++ b/src/napari_imagej/widget_IJ2.py
@@ -1,42 +1,61 @@
from enum import Enum
+from threading import Thread
from typing import List
from magicgui.widgets import request_values
from napari import Viewer
+from napari._qt.qt_resources import QColoredSVGIcon
from napari.layers import Layer
from qtpy.QtCore import Qt
from qtpy.QtGui import QIcon, QPixmap
-from qtpy.QtWidgets import QMessageBox, QPushButton, QVBoxLayout, QWidget
+from qtpy.QtWidgets import QHBoxLayout, QMessageBox, QPushButton, QWidget
from napari_imagej._module_utils import _get_layers_hack
-from napari_imagej.setup_imagej import ij, jc, running_headless
+from napari_imagej.setup_imagej import ensure_jvm_started, ij, jc, running_headless
class GUIWidget(QWidget):
def __init__(self, viewer: Viewer):
super().__init__()
- self.setLayout(QVBoxLayout())
+ self.setLayout(QHBoxLayout())
+
+ self.from_ij: FromIJButton = FromIJButton(viewer)
+ self.layout().addWidget(self.from_ij)
+
+ self.to_ij: ToIJButton = ToIJButton(viewer)
+ self.layout().addWidget(self.to_ij)
self.gui_button: GUIButton = GUIButton()
self.layout().addWidget(self.gui_button)
- self.to_ij: ToIJButton = ToIJButton()
- self.layout().addWidget(self.to_ij)
+ if running_headless():
+ self.gui_button.clicked.connect(self.gui_button.disable_popup)
+ else:
+ self.gui_button.clicked.connect(self._showUI)
+ self.gui_button.clicked.connect(lambda: self.to_ij.setEnabled(True))
+ self.gui_button.clicked.connect(lambda: self.from_ij.setEnabled(True))
- self.from_ij: FromIJButton = FromIJButton(viewer)
- self.layout().addWidget(self.from_ij)
- if not running_headless():
- self.gui_button.clicked.connect(lambda: self.to_ij.setHidden(False))
- self.gui_button.clicked.connect(lambda: self.from_ij.setHidden(False))
+ def _showUI(self):
+ """
+ NB: This must be its own function to prevent premature calling of ij()
+ """
+ ensure_jvm_started()
+ ij().ui().showUI()
class ToIJButton(QPushButton):
- def __init__(self):
+ def __init__(self, viewer):
super().__init__()
- self.setHidden(True)
- self.setText("Send layers to ImageJ2")
+ self.setEnabled(False)
+ icon = QColoredSVGIcon.from_resources("long_right_arrow")
+ self.setIcon(icon.colored(theme=viewer.theme))
+ self.setToolTip("Send layers to ImageJ2")
self.clicked.connect(self.send_layers)
+ def _set_icon(self, path: str):
+ icon: QIcon = QIcon(QPixmap(path))
+ self.setIcon(icon)
+
def send_layers(self):
layers: dict = request_values(
title="Send layers to ImageJ2",
@@ -55,10 +74,16 @@ def __init__(self, viewer: Viewer):
super().__init__()
self.viewer = viewer
- self.setHidden(True)
- self.setText("Get layers from ImageJ2")
+ self.setEnabled(False)
+ icon = QColoredSVGIcon.from_resources("long_left_arrow")
+ self.setIcon(icon.colored(theme=viewer.theme))
+ self.setToolTip("Get layers from ImageJ2")
self.clicked.connect(self.get_layers)
+ def _set_icon(self, path: str):
+ icon: QIcon = QIcon(QPixmap(path))
+ self.setIcon(icon)
+
def _get_objects(self, t):
compatibleInputs = ij().convert().getCompatibleInputs(t)
compatibleInputs.addAll(ij().object().getObjects(t))
@@ -87,28 +112,30 @@ def get_layers(self) -> List[Layer]:
class GUIButton(QPushButton):
def __init__(self):
super().__init__()
- self._text = "Display ImageJ2 GUI"
-
- if running_headless():
- self._setup_headless()
- else:
+ running_headful = not running_headless()
+ self.setEnabled(False)
+ if running_headful:
self._setup_headful()
+ else:
+ self._setup_headless()
def _set_icon(self, path: str):
icon: QIcon = QIcon(QPixmap(path))
self.setIcon(icon)
def _setup_headful(self):
- self._set_icon("resources/16x16-flat.png")
- self.setText(self._text)
- self.setToolTip("Open ImageJ2 in a new window!")
- self.clicked.connect(ij().ui().showUI)
+ self._set_icon("resources/16x16-flat-disabled.png")
+ self.setToolTip("Display ImageJ2 GUI (loading)")
+ def post_setup():
+ ensure_jvm_started()
+ self._set_icon("resources/16x16-flat.png")
+ self.setEnabled(True)
+ self.setToolTip("Display ImageJ2 GUI")
+ Thread(target=post_setup).start()
def _setup_headless(self):
self._set_icon("resources/16x16-flat-disabled.png")
- self.setText(self._text + " (disabled)")
- self.setToolTip("Not available when running PyImageJ headlessly!")
- self.clicked.connect(self.disable_popup)
+ self.setToolTip("ImageJ2 GUI unavailable!")
def disable_popup(self):
msg: QMessageBox = QMessageBox()
diff --git a/tests/test_IJ2GUIWidget.py b/tests/test_IJ2GUIWidget.py
index aaa1d7e0..dd2ea523 100644
--- a/tests/test_IJ2GUIWidget.py
+++ b/tests/test_IJ2GUIWidget.py
@@ -4,7 +4,7 @@
from napari.viewer import current_viewer
from qtpy.QtCore import Qt, QTimer
from qtpy.QtGui import QPixmap
-from qtpy.QtWidgets import QApplication, QDialog, QMessageBox, QPushButton, QVBoxLayout
+from qtpy.QtWidgets import QApplication, QDialog, QMessageBox, QPushButton, QHBoxLayout
from napari_imagej.setup_imagej import JavaClasses, running_headless
from napari_imagej.widget_IJ2 import FromIJButton, GUIButton, GUIWidget, ToIJButton
@@ -62,10 +62,10 @@ def test_widget_layout(gui_widget: GUIWidget):
"""Tests the number and expected order of imagej_widget children"""
subwidgets = gui_widget.children()
assert len(subwidgets) == 4
- assert isinstance(subwidgets[0], QVBoxLayout)
- assert isinstance(subwidgets[1], GUIButton)
+ assert isinstance(subwidgets[0], QHBoxLayout)
+ assert isinstance(subwidgets[1], FromIJButton)
assert isinstance(subwidgets[2], ToIJButton)
- assert isinstance(subwidgets[3], FromIJButton)
+ assert isinstance(subwidgets[3], GUIButton)
@pytest.mark.skipif(
@@ -79,10 +79,9 @@ def test_GUIButton_layout_headful(qtbot, ij, gui_widget: GUIWidget):
actual: QPixmap = button.icon().pixmap(expected.size())
assert expected.toImage() == actual.toImage()
- expected_text = "Display ImageJ2 GUI"
- assert expected_text == button.text()
+ assert "" == button.text()
- expected_toolTip = "Open ImageJ2 in a new window!"
+ expected_toolTip = "Display ImageJ2 GUI"
assert expected_toolTip == button.toolTip()
# Test showing UI
@@ -102,11 +101,10 @@ def test_GUIButton_layout_headless(qtbot, gui_widget: GUIWidget):
actual: QPixmap = button.icon().pixmap(expected.size())
assert expected.toImage() == actual.toImage()
- expected_text = "Display ImageJ2 GUI (disabled)"
- assert expected_text == button.text()
+ assert "" == button.text()
- expected_toolTip = "Not available when running PyImageJ headlessly!"
- assert expected_toolTip == button.toolTip()
+ expected_text = "ImageJ2 GUI unavailable!"
+ assert expected_text == button.toolTip()
# Test popup when running headlessly
def handle_dialog():
@@ -137,11 +135,11 @@ def handle_dialog():
)
def test_data_to_ImageJ(qtbot, ij, gui_widget: GUIWidget):
button: ToIJButton = gui_widget.to_ij
- assert button.isHidden()
+ assert not button.isEnabled()
# Show the button
qtbot.mouseClick(gui_widget.gui_button, Qt.LeftButton, delay=1)
- qtbot.waitUntil(lambda: not button.isHidden())
+ qtbot.waitUntil(lambda: button.isEnabled())
# Add some data to the viewer
sample_data = numpy.ones((100, 100, 3))
@@ -174,11 +172,11 @@ def handle_dialog():
)
def test_data_from_ImageJ(qtbot, ij, gui_widget: GUIWidget):
button: FromIJButton = gui_widget.from_ij
- assert button.isHidden()
+ assert not button.isEnabled()
# Show the button
qtbot.mouseClick(gui_widget.gui_button, Qt.LeftButton, delay=1)
- qtbot.waitUntil(lambda: not button.isHidden())
+ qtbot.waitUntil(lambda: button.isEnabled())
# Add some data to ImageJ
sample_data = jc.ArrayImgs.bytes(10, 10, 10)