42
42
from enum import Enum
43
43
from functools import lru_cache
44
44
from pathlib import Path
45
+ from textwrap import dedent
45
46
from typing import Tuple , Union
46
47
47
48
import numpy as np
@@ -1205,6 +1206,10 @@ def init(
1205
1206
1206
1207
ij = imagej.init("sc.fiji:fiji", mode=imagej.Mode.GUI)
1207
1208
"""
1209
+ force = mode .endswith (":force" )
1210
+ if force :
1211
+ mode = mode [:- 6 ]
1212
+
1208
1213
if headless is not None :
1209
1214
_logger .warning (
1210
1215
"The headless flag of imagej.init is deprecated. "
@@ -1215,10 +1220,31 @@ def init(
1215
1220
macos = sys .platform == "darwin"
1216
1221
1217
1222
if macos and mode == Mode .INTERACTIVE :
1218
- # check for main thread only on macOS
1219
- if _macos_is_main_thread ():
1223
+ if not _macos_enable_interactive (force = force ):
1220
1224
raise EnvironmentError (
1221
- "Sorry, the interactive mode is not available on macOS."
1225
+ dedent ("""\
1226
+ Cannot enable interactive mode in this environment.
1227
+ On macOS, the CoreFoundation/AppKit event loop must
1228
+ be running on the process's main thread.
1229
+
1230
+ If you are in an IPython/Jupyter environment,
1231
+ you can try using the `%gui osx` magic.
1232
+
1233
+ If you are running with plain python, your program
1234
+ appears to be running on the process's main thread,
1235
+ meaning your program will hang when attempting to
1236
+ perform any GUI-related action like showing the UI.
1237
+ You could try launching via Jaunch...
1238
+
1239
+ Or, if you really want to try it anyway, you can
1240
+ pass `mode="interactive:force"` for the init mode,
1241
+ which will bypass this check. But this mode is
1242
+ UNSUPPORTED and the PyImageJ team CANNOT SUPPORT YOU
1243
+ if you do this. Please do not publish scripts that
1244
+ use `interactive:force`; rather, let's improve
1245
+ PyImageJ's `_macos_enable_interactive()` function
1246
+ to more smartly detect your deployment situation.
1247
+ """ )
1222
1248
)
1223
1249
1224
1250
if not sj .jvm_started ():
@@ -1551,25 +1577,50 @@ def _includes_imagej_legacy(items: list):
1551
1577
return any (item .startswith ("net.imagej:imagej-legacy" ) for item in items )
1552
1578
1553
1579
1554
- def _macos_is_main_thread ():
1555
- """Detect if the current thread is the main thread on macOS.
1580
+ def _macos_enable_interactive (force : bool = False ) -> bool :
1581
+ """
1582
+ Make interactive mode work on macOS if possible.
1556
1583
1557
- :return: Boolean indicating if the current thread is the main thread .
1584
+ :return: True if interactive mode will work, False if not .
1558
1585
"""
1559
- # try to load the pthread library
1586
+ _logger .debug ("Attempting to enable interactive mode." )
1587
+
1588
+ # Check for an IPython/Jupyter environment.
1560
1589
try :
1590
+ import IPython
1591
+
1592
+ ipy = IPython .get_ipython ()
1593
+ if ipy is None :
1594
+ _logger .debug ("No IPython environment found." )
1595
+ else :
1596
+ # Engage the `%gui osx` magic!
1597
+ ipy .enable_gui ("osx" )
1598
+ _logger .debug ("Enabled IPython osx gui." )
1599
+ return True
1600
+ except Exception as exc :
1601
+ _logger .debug ("Failed to converse with IPython." )
1602
+ _logger .debug (exc )
1603
+
1604
+ # Try to ask the pthread library.
1605
+ try :
1606
+ # Ask pthread whether we're on the main thread.
1561
1607
pthread = cdll .LoadLibrary ("libpthread.dylib" )
1608
+ thread_num = pthread .pthread_main_np ()
1609
+ _logger .debug (f"pthread reports thread number { thread_num } ." )
1610
+ if thread_num != 1 :
1611
+ # Not on the main thread! Worth a try...
1612
+ return True
1562
1613
except OSError as exc :
1563
- _log_exception (_logger , exc )
1564
- print ("No pthread library found." )
1565
- # assume the current thread is the main thread
1566
- return True
1614
+ _logger .debug ("Failed to converse with pthread library." )
1615
+ _logger .debug (exc )
1567
1616
1568
- # detect if the current thread is the main thread
1569
- if pthread .pthread_main_np () == 1 :
1617
+ # Seems like we're on the main thread outside IPython.
1618
+ if force :
1619
+ _logger .warning ("Interactive mode forced. Your program might hang." )
1570
1620
return True
1571
- else :
1572
- return False
1621
+
1622
+ _logger .debug ("All checks failed. Interactive mode not available." )
1623
+ return False
1573
1624
1574
1625
1575
1626
def _set_ij_env (ij_dir ):
0 commit comments