Open ctrueden opened 2 years ago
Side note from @tlambert03 from a Zulip thread where we discussed this issue:
Re running a Qt app without blocking the main thread: that’s thanks to this chunk of code here: https://github.com/ipython/ipython/blob/master/IPython/terminal/pt_inputhooks/qt.py
It’s what makes the “gui qt” magic work too https://ipython.readthedocs.io/en/stable/config/eventloops.html
While debugging this problem, at one point when creating multiple SciJava contexts in the same JVM I saw this message:
2022-04-30 12:16:19.092 python[96732:3183402] [JRSAppKitAWT markAppIsDaemon]: Process manager already initialized: can't fully enable headless mode.
I narrowed down the problem to specific SciJava services:
import imagej
#ij = imagej.init("2.5.0", add_legacy=False)
imagej._create_jvm("2.5.0", mode='headless', add_legacy=False)
#-----
from jpype import JArray
def jarray(plist, element_type):
array = JArray(element_type)(len(plist))
for i in range(len(plist)):
array[i] = plist[i]
return array
#-----
from scyjava import jimport
fail_services = [
'org.scijava.event.DefaultEventHistory',
'org.scijava.command.DefaultCommandService',
]
Class = jimport('java.lang.Class')
services = jarray([jimport(s) for s in fail_services], Class)
Context = jimport('org.scijava.Context')
ctx = Context(services)
#-----
import napari, skimage
napari.view_image(skimage.data.cells3d())
But I found that the order of service initialization, not just which services, can affect whether napari works afterward. For example, if you change the above to:
fail_services = [
'org.scijava.command.DefaultCommandService',
'org.scijava.event.DefaultEventHistory',
]
Then napari still works.
Further digging needed to understand what's happening when as these services are constructed and initialized.
I did a little debugging. Turns out that the order of fail_services
actually affects the screen size as reported by quartz
.
DefaultCommandService
is first, we have a nonzero DPI (due to nonzero screensize).DefaultEventHistory
is first, we have a zero DPI (due to a screensize of (0, 0)).On e.g. linux, vispy calls a different function. This guy runs xdpyinfo
, returning a DPI of 96x96 in both cases.
I did some more hacking to your notebook example, which I turned into a script:
import imagej
from scyjava import config
# config.add_option('-agentlib:jdwp=transport=dt_socket,server=y,address=5286,suspend=y')
#ij = imagej.init("2.5.0", add_legacy=False)
imagej._create_jvm("2.5.0", mode='headless', add_legacy=False)
from jpype import JArray
def jarray(plist, element_type):
array = JArray(element_type)(len(plist))
for i in range(len(plist)):
array[i] = plist[i]
return array
from scyjava import jimport
fail_services = [
'org.scijava.event.DefaultEventHistory',
'org.scijava.command.DefaultCommandService',
]
Class = jimport('java.lang.Class')
services = jarray([jimport(s) for s in fail_services], Class)
Context = jimport('org.scijava.Context')
ctx = Context(jarray([], Class))
# BEGIN NEW STUFF
ServiceHelper = jimport('org.scijava.service.ServiceHelper')
Collections = jimport('java.util.Collections')
from vispy.ext.cocoapy import quartz
for c in services:
ServiceHelper(ctx, Collections.singletonList(c)).loadServices()
screensize = quartz.CGDisplayScreenSize(quartz.CGMainDisplayID())
print(f"Screen size after loading {c}: {screensize.width} x {screensize.height}")
screensize = quartz.CGDisplayScreenSize(quartz.CGMainDisplayID())
print(f"Screen size after loading {c}: {screensize.width} x {screensize.height}")
# END NEW STUFF
import napari, skimage
napari.view_image(skimage.data.cells3d())
this throws some crazy error:
Screen size after loading class org.scijava.event.DefaultEventHistory: 602.0740650318287 x 341.1940247265261
Screen size after loading class org.scijava.command.DefaultCommandService: 602.0740650318287 x 341.1940247265261
Screen size after loading class org.scijava.command.DefaultCommandService: 602.0740650318287 x 341.1940247265261
/Users/gselzer/miniconda3/envs/napari-imagej-dev/lib/python3.9/site-packages/skimage/io/manage_plugins.py:23: UserWarning: Your installed pillow version is < 8.1.2. Several security issues (CVE-2021-27921, CVE-2021-25290, CVE-2021-25291, CVE-2021-25293, and more) have been fixed in pillow 8.1.2 or higher. We recommend to upgrade this library.
from .collection import imread_collection_wrapper
Screen size width: 602.0740650318287; height: 341.1940247265261
Screen size dpi: 67.25000100999367
2022-05-02 17:09:04.934 python[96234:7598623] *** Assertion failure in -[NSThemeFrame initWithFrame:], NSView.m:1353
2022-05-02 17:09:04.936 python[96234:7598623] *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Frame containing non-finite values {{0, 0}, {640, nan}} passed to [NSThemeFrame initWithFrame:]'
*** First throw call stack:
(
0 CoreFoundation 0x00007fff204ef29b __exceptionPreprocess + 242
1 libobjc.A.dylib 0x00007fff20228d92 objc_exception_throw + 48
2 CoreFoundation 0x00007fff20518422 +[NSException raise:format:arguments:] + 88
3 Foundation 0x00007fff212d74e2 -[NSAssertionHandler handleFailureInMethod:object:file:lineNumber:description:] + 191
4 AppKit 0x00007fff22c94350 -[NSView initWithFrame:] + 482
5 AppKit 0x00007fff22c94072 -[NSFrameView initWithFrame:styleMask:owner:] + 74
6 AppKit 0x00007fff22c93eeb -[NSThemeFrame initWithFrame:styleMask:owner:] + 72
7 AppKit 0x00007fff22c91d82 -[NSWindow _commonInitFrame:styleMask:backing:defer:] + 633
8 AppKit 0x00007fff22c915dc -[NSWindow _initContent:styleMask:backing:defer:contentView:] + 1098
9 AppKit 0x00007fff22c9118b -[NSWindow initWithContentRect:styleMask:backing:defer:] + 42
10 AppKit 0x00007fff22f9b43c -[NSWindow initWithContentRect:styleMask:backing:defer:screen:] + 52
11 libqcocoa.dylib 0x00000009c7a7e725 -[QNSWindow initWithContentRect:styleMask:backing:defer:screen:platformWindow:] + 197
12 libqcocoa.dylib 0x00000009c7a6bcac _ZN12QCocoaWindow14createNSWindowEb + 1324
13 libqcocoa.dylib 0x00000009c7a65672 _ZN12QCocoaWindow22recreateWindowIfNeededEv + 1266
14 libqcocoa.dylib 0x00000009c7a650fe _ZN12QCocoaWindow10initializeEv + 318
15 QtGui 0x00000009c6215eb7 _ZN14QWindowPrivate6createEby + 151
16 QtWidgets 0x00000009c6e49771 _ZN14QWidgetPrivate6createEv + 1185
17 QtWidgets 0x00000009c6e48414 _ZN7QWidget6createEybb + 324
18 QtWidgets 0x00000009c6f702c7 _ZN11QMainWindow30setUnifiedTitleAndToolBarOnMacEb + 55
19 QtWidgets.abi3.so 0x00000009c6a59307 _ZL47meth_QMainWindow_setUnifiedTitleAndToolBarOnMacP7_objectS0_ + 87
20 python 0x000000010b8c296a cfunction_call + 90
21 python 0x000000010b8638b6 _PyObject_MakeTpCall + 134
22 python 0x000000010b99b5d7 call_function + 311
23 python 0x000000010b994348 _PyEval_EvalFrameDefault + 27608
24 python 0x000000010b98be09 _PyEval_EvalCode + 473
25 python 0x000000010b86453c _PyFunction_Vectorcall + 236
26 python 0x000000010b863b4d _PyObject_FastCallDictTstate + 109
27 python 0x000000010b8ed7a0 slot_tp_init + 192
28 python 0x000000010b8f89f0 type_call + 272
29 python 0x000000010b8638b6 _PyObject_MakeTpCall + 134
30 python 0x000000010b99b5d7 call_function + 311
31 python 0x000000010b9935dd _PyEval_EvalFrameDefault + 24173
32 python 0x000000010b98be09 _PyEval_EvalCode + 473
33 python 0x000000010b86453c _PyFunction_Vectorcall + 236
34 python 0x000000010b863bcd _PyObject_FastCallDictTstate + 237
35 python 0x000000010b8ed7a0 slot_tp_init + 192
36 python 0x000000010b8f89f0 type_call + 272
37 python 0x000000010b8638b6 _PyObject_MakeTpCall + 134
38 python 0x000000010b99b5d7 call_function + 311
39 python 0x000000010b98e54e _PyEval_EvalFrameDefault + 3550
40 python 0x000000010b98be09 _PyEval_EvalCode + 473
41 python 0x000000010b86453c _PyFunction_Vectorcall + 236
42 python 0x000000010b863b4d _PyObject_FastCallDictTstate + 109
43 python 0x000000010b8ed7a0 slot_tp_init + 192
44 python 0x000000010b8f89f0 type_call + 272
45 python 0x000000010b86430c _PyObject_Call + 108
46 python 0x000000010b992dc2 _PyEval_EvalFrameDefault + 22098
47 python 0x000000010b98be09 _PyEval_EvalCode + 473
48 python 0x000000010b86453c _PyFunction_Vectorcall + 236
49 python 0x000000010b99b54e call_function + 174
50 python 0x000000010b9935dd _PyEval_EvalFrameDefault + 24173
51 python 0x000000010b98be09 _PyEval_EvalCode + 473
52 python 0x000000010b86453c _PyFunction_Vectorcall + 236
53 python 0x000000010b99b54e call_function + 174
54 python 0x000000010b994348 _PyEval_EvalFrameDefault + 27608
55 python 0x000000010b98be09 _PyEval_EvalCode + 473
56 python 0x000000010b9fc003 pyrun_file + 339
57 python 0x000000010b9fb7e8 pyrun_simple_file + 408
58 python 0x000000010b9fb5fd PyRun_SimpleFileExFlags + 109
59 python 0x000000010ba24b49 pymain_run_file + 329
60 python 0x000000010ba24101 pymain_run_python + 417
61 python 0x000000010ba23f15 Py_RunMain + 37
62 python 0x000000010ba25590 pymain_main + 64
63 python 0x000000010b7f95b8 main + 56
64 libdyld.dylib 0x00007fff20398f3d start + 1
)
libc++abi: terminating with uncaught exception of type NSException
zsh: abort python tmp.py
The more interesting thing, though, is if you remove some of the print statements, you get different returns. I get this printout when I run the script, after removing the screen size checks in the for
loop. I wonder if some caching is going on here...
(napari-imagej-dev) gselzer@dyn-144-92-48-223 imagej % python tmp.py
Screen size after loading class org.scijava.command.DefaultCommandService: 0.0 x 0.0
/Users/gselzer/miniconda3/envs/napari-imagej-dev/lib/python3.9/site-packages/skimage/io/manage_plugins.py:23: UserWarning: Your installed pillow version is < 8.1.2. Several security issues (CVE-2021-27921, CVE-2021-25290, CVE-2021-25291, CVE-2021-25293, and more) have been fixed in pillow 8.1.2 or higher. We recommend to upgrade this library.
from .collection import imread_collection_wrapper
WARNING: no screens available, assuming 24-bit color
Screen size width: 0.0; height: 0.0
Traceback (most recent call last):
File "/Users/gselzer/code/imagej/tmp.py", line 41, in <module>
napari.view_image(skimage.data.cells3d())
File "/Users/gselzer/miniconda3/envs/napari-imagej-dev/lib/python3.9/site-packages/napari/view_layers.py", line 143, in view_image
return _make_viewer_then('add_image', args, kwargs)
File "/Users/gselzer/miniconda3/envs/napari-imagej-dev/lib/python3.9/site-packages/napari/view_layers.py", line 122, in _make_viewer_then
viewer = Viewer(**vkwargs)
File "/Users/gselzer/miniconda3/envs/napari-imagej-dev/lib/python3.9/site-packages/napari/viewer.py", line 57, in __init__
self._window = Window(self, show=show)
File "/Users/gselzer/miniconda3/envs/napari-imagej-dev/lib/python3.9/site-packages/napari/_qt/qt_main_window.py", line 418, in __init__
self._qt_window = _QtMainWindow(viewer)
File "/Users/gselzer/miniconda3/envs/napari-imagej-dev/lib/python3.9/site-packages/napari/_qt/qt_main_window.py", line 81, in __init__
self._qt_viewer = QtViewer(viewer, show_welcome_screen=True)
File "/Users/gselzer/miniconda3/envs/napari-imagej-dev/lib/python3.9/site-packages/napari/_qt/qt_viewer.py", line 263, in __init__
self._create_canvas()
File "/Users/gselzer/miniconda3/envs/napari-imagej-dev/lib/python3.9/site-packages/napari/_qt/qt_viewer.py", line 350, in _create_canvas
self.canvas = VispyCanvas(
File "/Users/gselzer/miniconda3/envs/napari-imagej-dev/lib/python3.9/site-packages/napari/_vispy/canvas.py", line 34, in __init__
super().__init__(*args, **kwargs)
File "/Users/gselzer/miniconda3/envs/napari-imagej-dev/lib/python3.9/site-packages/vispy/scene/canvas.py", line 135, in __init__
super(SceneCanvas, self).__init__(
File "/Users/gselzer/miniconda3/envs/napari-imagej-dev/lib/python3.9/site-packages/vispy/app/canvas.py", line 143, in __init__
dpi = get_dpi(raise_error=False)
File "/Users/gselzer/miniconda3/envs/napari-imagej-dev/lib/python3.9/site-packages/vispy/util/dpi/_quartz.py", line 27, in get_dpi
dpi = (px.width/mm.width + px.height/mm.height) * 0.5 * 25.4
ZeroDivisionError: float division by zero
I can probably look more tomorrow.
Here is a minimal example:
The
view_image
call then fails. First we seeWARNING: no screens available, assuming 24-bit color
, and then deep in the stack trace:returns 0, and then
ZeroDivisionError: float division by zero
WORKAROUND: start napari first:
But I found that with the workaround, trying to change the color map caused the napari GUI to hang.