bpurinton / PebbleCounts

PebbleCounts: grain-sizing algorithm for gravel-bed river imagery
GNU General Public License v3.0
12 stars 9 forks source link

Python crashes when opening window on Mac with solution #10

Open geosjackson opened 3 years ago

geosjackson commented 3 years ago

I am trying to run PebbleCounts on a 2013 Mac Pro (Catalina 10.15.7). I followed the installation instructions for Mac verbatim from the manual, and found that both PebbleCounts.py and PebbleCountsAuto.py run smoothly up until the program needs to open a window, at which point Python crashes and throws the following exception:

2021-06-17 10:06:50.916 python[11061:572913] -[NSApplication macMinorVersion]: unrecognized selector sent to instance 0x7ff0c5b33d90
2021-06-17 10:06:50.940 python[11061:572913] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[NSApplication macMinorVersion]: unrecognized selector sent to instance 0x7ff0c5b33d90'
*** First throw call stack:
(
    0   CoreFoundation                      0x00007fff204ee6af __exceptionPreprocess + 242
    1   libobjc.A.dylib                     0x00007fff202263c9 objc_exception_throw + 48
    2   CoreFoundation                      0x00007fff20570c85 -[NSObject(NSObject) __retain_OA] + 0
    3   CoreFoundation                      0x00007fff2045607d ___forwarding___ + 1467
    4   CoreFoundation                      0x00007fff20455a38 _CF_forwarding_prep_0 + 120
    5   libtk8.6.dylib                      0x0000001a2ff0edb9 SetCGColorComponents + 265
    6   libtk8.6.dylib                      0x0000001a2ff0f67a TkpGetColor + 250
    7   libtk8.6.dylib                      0x0000001a2fe49aa9 Tk_GetColor + 153
    8   libtk8.6.dylib                      0x0000001a2fe398e6 Tk_Get3DBorder + 134
    9   libtk8.6.dylib                      0x0000001a2fe3974f Tk_Alloc3DBorderFromObj + 127
    10  libtk8.6.dylib                      0x0000001a2fe4afad DoObjConfig + 941
    11  libtk8.6.dylib                      0x0000001a2fe4aae5 Tk_InitOptions + 357
    12  libtk8.6.dylib                      0x0000001a2fe4a9c5 Tk_InitOptions + 69
    13  libtk8.6.dylib                      0x0000001a2fe7bb5c CreateFrame + 1548
    14  libtk8.6.dylib                      0x0000001a2fe7be37 TkListCreateFrame + 151
    15  libtk8.6.dylib                      0x0000001a2fe737f8 Initialize + 2168
    16  _tkinter.cpython-36m-darwin.so      0x0000001a2fca5a16 _tkinter_create + 1174
    17  python                              0x0000000109a3dd48 _PyCFunction_FastCallDict + 200
    18  python                              0x0000000109b1339f call_function + 143
    19  python                              0x0000000109b10ef4 _PyEval_EvalFrameDefault + 46836
    20  python                              0x0000000109b04649 _PyEval_EvalCodeWithName + 425
    21  python                              0x0000000109b1403c _PyFunction_FastCallDict + 364
    22  python                              0x00000001099bcdc0 _PyObject_FastCallDict + 320
    23  python                              0x00000001099e43d8 method_call + 136
    24  python                              0x00000001099c43de PyObject_Call + 62
    25  python                              0x0000000109a65285 slot_tp_init + 117
    26  python                              0x0000000109a697c1 type_call + 241
    27  python                              0x00000001099bcd31 _PyObject_FastCallDict + 177
    28  python                              0x0000000109b13498 call_function + 392
    29  python                              0x0000000109b10ef4 _PyEval_EvalFrameDefault + 46836
    30  python                              0x0000000109b04649 _PyEval_EvalCodeWithName + 425
    31  python                              0x0000000109b137fa fast_function + 362
    32  python                              0x0000000109b133fc call_function + 236
    33  python                              0x0000000109b10ef4 _PyEval_EvalFrameDefault + 46836
    34  python                              0x0000000109b1374c fast_function + 188
    35  python                              0x0000000109b133fc call_function + 236
    36  python                              0x0000000109b10ef4 _PyEval_EvalFrameDefault + 46836
    37  python                              0x0000000109b04649 _PyEval_EvalCodeWithName + 425
    38  python                              0x0000000109b5d12c PyRun_FileExFlags + 252
    39  python                              0x0000000109b5c604 PyRun_SimpleFileExFlags + 372
    40  python                              0x0000000109b83346 Py_Main + 3734
    41  python                              0x00000001099b4d99 main + 313
    42  libdyld.dylib                       0x00007fff20397621 start + 1
)
libc++abi.dylib: terminating with uncaught exception of type NSException

I have reproduced everything on a 2019 MacBook Pro (Big Sur 11.2.1), and the errors are identical between the two machines.

I have found discussions of matplotlib backend issues on Mac, which generally advise using TkAgg (https://github.com/MTG/sms-tools/issues/36). This solution is already implemented in PebbleCounts.py, and did not work for me. I am uncertain as to why Tk in general seems not to work in my pebblecounts Conda environment -- perhaps backwards incompatibility between Python 3.6 and Tk 8.6? Created as written in the manual, the pebblecounts environment uses Python 3.6.10 and Tk 8.6.10, both Conda’s default packages. These versions seem to be the only viable options, as PebbleCounts requires Python 3.6 for all necessary dependencies to work and Tk 8.5 has serious bugs that affect macOS (https://www.python.org/download/mac/tcltk/).

Solution

While I have not been able to get Tk to work in the pebblecounts environment, I was able to get PebbleCounts.py and PebbleCountsAuto.py running smoothly by making two edits:

  1. In PCfunctions.py, omitting the portion of resizeWin() that uses tkinter:

Original resizeWin()

def resizeWin(img, resize_factor=0.7):
    """
    Get the system screen resolution and resize input image by a factor. Factor
    must be in the range (0, 1].
    """
    from sys import platform as sys_pf
    if 'win' in sys_pf:
        # this import needs to be within the function or else openCV throws errors
        import tkinter as tk
        root = tk.Tk()
        resize_factor = (1 - resize_factor) + 1
        sys_w, sys_h = root.winfo_screenwidth()/resize_factor, root.winfo_screenheight()/resize_factor
        root.destroy()
        root.quit()
        del root
    else:
        resize_factor = (1 - resize_factor) + 1
        sys_w, sys_h = 1920/resize_factor, 1080/resize_factor
    scale_width = sys_w / img.shape[1]
    scale_height = sys_h / img.shape[0]
    dimensions = min(scale_width, scale_height)
    window_width = int(img.shape[1] * dimensions)
    window_height = int(img.shape[0] * dimensions)
    return window_width, window_height

Edited resizeWin()

def resizeWin(img, resize_factor=0.7):
    """
    Get the system screen resolution and resize input image by a factor. Factor
    must be in the range (0, 1].
    """
    resize_factor = (1 - resize_factor) + 1
    sys_w, sys_h = 1920/resize_factor, 1080/resize_factor
    scale_width = sys_w / img.shape[1]
    scale_height = sys_h / img.shape[0]
    dimensions = min(scale_width, scale_height)
    window_width = int(img.shape[1] * dimensions)
    window_height = int(img.shape[0] * dimensions)
    return window_width, window_height
  1. In PebbleCounts.py, omitting the statement shown below that sets the matplotlib backend to TkAgg on Mac:
# use a different backend for matplotlib on MacOS
from sys import platform as sys_pf
if sys_pf == 'darwin':
   import matplotlib
   matplotlib.use('TkAgg')

Without this statement, matplotlib uses the MacOSX backend.

>>> import matplotlib
>>> matplotlib.get_backend()
'MacOSX'
bpurinton commented 3 years ago

Thanks a lot of looking into this (I'm sure it took some time...)

I have had very similar issues in the past with compatibility between openCV, tkinter, and matplotlib. Most errors that have occurred for myself and other users have come at the window pop up stage with a "NSException".

Because this has been an ongoing issue (which I have previously changed bits of the code for), I will refrain from modifying and git-pushing a change to the code at this time. I fear that any change may break PebbleCounts on other machines, whereas nice comments like this can serve as a guide for other users to change a few import lines in the base code and try to get it working themselves.

Of course, as always, I'm available on GitHub or by email for trouble shooting.

Thanks again!