haesleinhuepf/napari-laptrack

Connect plugin to Arboretum (napari-arboretum)

Opened this issue · 1 comments

After generating a tracking result with this plugin it would be cool to visualize the lineage tree in Arboretum (napari-arboretum). Currently, if I open arboretum and double click on a track in the Tracks layer, for example track_ID = 5, I get the following output:
connection_laptrack_arboretum
(cancer cell migration dataset from Tinevez, J. & Guillaume Jacquemet, G. licensed by CC BY 4.0)

And this Error message:

KeyError                                  Traceback (most recent call last)
File ~\mambaforge\envs\laptrack_env2\lib\site-packages\pandas\core\indexes\base.py:3802, in Index.get_loc(self=Index(['track_id'], dtype='object'), key='t', method=None, tolerance=None)
   3801 try:
-> 3802     return self._engine.get_loc(casted_key)
        casted_key = 't'
        self = Index(['track_id'], dtype='object')
   3803 except KeyError as err:

File ~\mambaforge\envs\laptrack_env2\lib\site-packages\pandas\_libs\index.pyx:138, in pandas._libs.index.IndexEngine.get_loc()

File ~\mambaforge\envs\laptrack_env2\lib\site-packages\pandas\_libs\index.pyx:165, in pandas._libs.index.IndexEngine.get_loc()

File pandas\_libs\hashtable_class_helper.pxi:5745, in pandas._libs.hashtable.PyObjectHashTable.get_item()

File pandas\_libs\hashtable_class_helper.pxi:5753, in pandas._libs.hashtable.PyObjectHashTable.get_item()

KeyError: 't'

The above exception was the direct cause of the following exception:

KeyError                                  Traceback (most recent call last)
File ~\mambaforge\envs\laptrack_env2\lib\site-packages\vispy\app\backends\_qt.py:522, in QtBaseCanvasBackend.mouseDoubleClickEvent(self=<vispy.app.backends._qt.CanvasBackendDesktop object>, ev=<PyQt5.QtGui.QMouseEvent object>)
    520 if self._vispy_canvas is None:
    521     return
--> 522 self._vispy_mouse_double_click(
        self = <vispy.app.backends._qt.CanvasBackendDesktop object at 0x000002AFE690D430>
        ev = <PyQt5.QtGui.QMouseEvent object at 0x000002AFE68DAE50>
        BUTTONMAP = {0: 0, 1: 1, 2: 2, 4: 3, 8: 4, 16: 5}
    523     native=ev,
    524     pos=_get_event_xy(ev),
    525     button=BUTTONMAP.get(ev.button(), 0),
    526     modifiers=self._modifiers(ev),
    527 )

File ~\mambaforge\envs\laptrack_env2\lib\site-packages\vispy\app\base.py:239, in BaseCanvasBackend._vispy_mouse_double_click(self=<vispy.app.backends._qt.CanvasBackendDesktop object>, **kwargs={'button': 1, 'buttons': [], 'last_event': <MouseEvent blocked=False button=1 buttons=[] de...es=[] time=1684328528.1467497 type=mouse_release>, 'last_mouse_press': None, 'modifiers': (), 'native': <PyQt5.QtGui.QMouseEvent object>, 'pos': (560, 319), 'press_event': None})
    235 def _vispy_mouse_double_click(self, **kwargs):
    236     # default method for delivering double-click events to the canvas
    237     kwargs.update(self._vispy_mouse_data)
--> 239     ev = self._vispy_canvas.events.mouse_double_click(**kwargs)
        self._vispy_canvas.events.mouse_double_click = <vispy.util.event.EventEmitter object at 0x000002AFE69087F0>
        kwargs = {'native': <PyQt5.QtGui.QMouseEvent object at 0x000002AFE68DAE50>, 'pos': (560, 319), 'button': 1, 'modifiers': (), 'buttons': [], 'press_event': None, 'last_event': <MouseEvent blocked=False button=1 buttons=[] delta=[0. 0.] handled=False is_dragging=True last_event=MouseEvent modifiers=() native=<PyQt5.QtGui.QMouseEvent object at 0x000002AFE68DAE50> pos=[560 319] press_event=MouseEvent source=None sources=[] time=1684328528.1467497 type=mouse_release>, 'last_mouse_press': None}
        self = <vispy.app.backends._qt.CanvasBackendDesktop object at 0x000002AFE690D430>
        self._vispy_canvas.events = <vispy.util.event.EmitterGroup object at 0x000002AFE69086D0>
        self._vispy_canvas = <VispyCanvas (PyQt5) at 0x2afe68f09d0>
    240     self._vispy_mouse_data['last_event'] = ev
    241     return ev

File ~\mambaforge\envs\laptrack_env2\lib\site-packages\vispy\util\event.py:453, in EventEmitter.__call__(self=<vispy.util.event.EventEmitter object>, *args=(), **kwargs={'button': 1, 'buttons': [], 'last_event': <MouseEvent blocked=False button=1 buttons=[] de...es=[] time=1684328528.1467497 type=mouse_release>, 'last_mouse_press': None, 'modifiers': (), 'native': <PyQt5.QtGui.QMouseEvent object>, 'pos': (560, 319), 'press_event': None})
    450 if self._emitting > 1:
    451     raise RuntimeError('EventEmitter loop detected!')
--> 453 self._invoke_callback(cb, event)
        event = <MouseEvent blocked=False button=1 buttons=[] delta=[0. 0.] handled=False is_dragging=False last_event=MouseEvent modifiers=() native=<PyQt5.QtGui.QMouseEvent object at 0x000002AFE68DAE50> pos=[560 319] press_event=None source=None sources=[] time=1684328528.2343628 type=mouse_double_click>
        self = <vispy.util.event.EventEmitter object at 0x000002AFE69087F0>
        cb = <bound method QtViewer.on_mouse_double_click of <napari._qt.qt_viewer.QtViewer object at 0x000002AFE68DACA0>>
    454 if event.blocked:
    455     break

File ~\mambaforge\envs\laptrack_env2\lib\site-packages\vispy\util\event.py:471, in EventEmitter._invoke_callback(self=<vispy.util.event.EventEmitter object>, cb=<bound method QtViewer.on_mouse_double_click of <napari._qt.qt_viewer.QtViewer object>>, event=<MouseEvent blocked=False button=1 buttons=[] de... time=1684328528.2343628 type=mouse_double_click>)
    469     cb(event)
    470 except Exception:
--> 471     _handle_exception(self.ignore_callback_errors,
        self = <vispy.util.event.EventEmitter object at 0x000002AFE69087F0>
        cb = <bound method QtViewer.on_mouse_double_click of <napari._qt.qt_viewer.QtViewer object at 0x000002AFE68DACA0>>
        event = <MouseEvent blocked=False button=1 buttons=[] delta=[0. 0.] handled=False is_dragging=False last_event=MouseEvent modifiers=() native=<PyQt5.QtGui.QMouseEvent object at 0x000002AFE68DAE50> pos=[560 319] press_event=None source=None sources=[] time=1684328528.2343628 type=mouse_double_click>
        (cb, event) = (<bound method QtViewer.on_mouse_double_click of <napari._qt.qt_viewer.QtViewer object at 0x000002AFE68DACA0>>, <MouseEvent blocked=False button=1 buttons=[] delta=[0. 0.] handled=False is_dragging=False last_event=MouseEvent modifiers=() native=<PyQt5.QtGui.QMouseEvent object at 0x000002AFE68DAE50> pos=[560 319] press_event=None source=None sources=[] time=1684328528.2343628 type=mouse_double_click>)
    472                       self.print_callback_errors,
    473                       self, cb_event=(cb, event))

File ~\mambaforge\envs\laptrack_env2\lib\site-packages\vispy\util\event.py:469, in EventEmitter._invoke_callback(self=<vispy.util.event.EventEmitter object>, cb=<bound method QtViewer.on_mouse_double_click of <napari._qt.qt_viewer.QtViewer object>>, event=<MouseEvent blocked=False button=1 buttons=[] de... time=1684328528.2343628 type=mouse_double_click>)
    467 def _invoke_callback(self, cb, event):
    468     try:
--> 469         cb(event)
        cb = <bound method QtViewer.on_mouse_double_click of <napari._qt.qt_viewer.QtViewer object at 0x000002AFE68DACA0>>
        event = <MouseEvent blocked=False button=1 buttons=[] delta=[0. 0.] handled=False is_dragging=False last_event=MouseEvent modifiers=() native=<PyQt5.QtGui.QMouseEvent object at 0x000002AFE68DAE50> pos=[560 319] press_event=None source=None sources=[] time=1684328528.2343628 type=mouse_double_click>
    470     except Exception:
    471         _handle_exception(self.ignore_callback_errors,
    472                           self.print_callback_errors,
    473                           self, cb_event=(cb, event))

File ~\mambaforge\envs\laptrack_env2\lib\site-packages\napari\_qt\qt_viewer.py:1057, in QtViewer.on_mouse_double_click(self=<napari._qt.qt_viewer.QtViewer object>, event=<MouseEvent blocked=False button=1 buttons=[] de... time=1684328528.2343628 type=mouse_double_click>)
   1038 def on_mouse_double_click(self, event):
   1039     """Called whenever a mouse double-click happen on the canvas
   1040
   1041     Parameters
   (...)
   1055          - mouse_release
   1056     """
-> 1057     self._process_mouse_event(mouse_double_click_callbacks, event)
        event = <MouseEvent blocked=False button=1 buttons=[] delta=[0. 0.] handled=False is_dragging=False last_event=MouseEvent modifiers=() native=<PyQt5.QtGui.QMouseEvent object at 0x000002AFE68DAE50> pos=[560 319] press_event=None source=None sources=[] time=1684328528.2343628 type=mouse_double_click>
        self = <napari._qt.qt_viewer.QtViewer object at 0x000002AFE68DACA0>

File ~\mambaforge\envs\laptrack_env2\lib\site-packages\napari\_qt\qt_viewer.py:1026, in QtViewer._process_mouse_event(self=<napari._qt.qt_viewer.QtViewer object>, mouse_callbacks=<function mouse_double_click_callbacks>, event=<ReadOnlyWrapper at 0x000002AF883FBD40 for MouseEvent>)
   1024 layer = self.viewer.layers.selection.active
   1025 if layer is not None:
-> 1026     mouse_callbacks(layer, event)
        event = <ReadOnlyWrapper at 0x000002AF883FBD40 for MouseEvent at 0x000002AF936B0E80>
        layer = <Tracks layer 'Tracks' at 0x2af88827ca0>
        mouse_callbacks = <function mouse_double_click_callbacks at 0x000002AFE285FE50>

File ~\mambaforge\envs\laptrack_env2\lib\site-packages\napari\utils\interactions.py:86, in mouse_double_click_callbacks(obj=<Tracks layer 'Tracks'>, event=<ReadOnlyWrapper at 0x000002AF883FBD40 for MouseEvent>)
     80 if inspect.isgeneratorfunction(mouse_click_func):
     81     raise ValueError(
     82         trans._(
     83             "Double-click actions can't be generators.", deferred=True
     84         )
     85     )
---> 86 mouse_click_func(obj, event)
        mouse_click_func = <function Arboretum.append_mouse_callback.<locals>.show_tree at 0x000002AF88FAA040>
        obj = <Tracks layer 'Tracks' at 0x2af88827ca0>
        event = <ReadOnlyWrapper at 0x000002AF883FBD40 for MouseEvent at 0x000002AF936B0E80>

File ~\mambaforge\envs\laptrack_env2\lib\site-packages\napari_arboretum\plugin.py:105, in Arboretum.append_mouse_callback.<locals>.show_tree(tracks=<Tracks layer 'Tracks'>, event=<ReadOnlyWrapper at 0x000002AF883FBD40 for MouseEvent>)
    101 track_id = tracks.get_value(cursor_position, world=True)
    102 if track_id is not None:
    103     # Setting this property automatically triggers re-drawing of the
    104     # tree and property graph
--> 105     self.track_id = track_id
        track_id = 1
        self = <napari_arboretum.plugin.Arboretum object at 0x000002AFF0442F70>
    106     self.draw_current_time_line()

File ~\mambaforge\envs\laptrack_env2\lib\site-packages\napari_arboretum\util.py:35, in TrackPropertyMixin.track_id(self=<napari_arboretum.plugin.Arboretum object>, track_id=1)
     32 @track_id.setter
     33 def track_id(self, track_id: int) -> None:
     34     self._track_id = track_id
---> 35     self.on_track_id_change()
        self = <napari_arboretum.plugin.Arboretum object at 0x000002AFF0442F70>

File ~\mambaforge\envs\laptrack_env2\lib\site-packages\napari_arboretum\plugin.py:68, in Arboretum.on_track_id_change(self=<napari_arboretum.plugin.Arboretum object>)
     66 def on_track_id_change(self):
     67     self.plotter.track_id = self.track_id
---> 68     self.property_plotter.track_id = self.track_id
        self = <napari_arboretum.plugin.Arboretum object at 0x000002AFF0442F70>
        self.property_plotter = <napari_arboretum.visualisation.matplotlib_plotter.MPLPropertyPlotter object at 0x000002AF88EF18B0>
     69     root_id = get_root_id(self.tracks, self.track_id)
     70     self.title.setText(f"Lineage Tree #{root_id}")

File ~\mambaforge\envs\laptrack_env2\lib\site-packages\napari_arboretum\util.py:35, in TrackPropertyMixin.track_id(self=<napari_arboretum.visualisation.matplotlib_plotter.MPLPropertyPlotter object>, track_id=1)
     32 @track_id.setter
     33 def track_id(self, track_id: int) -> None:
     34     self._track_id = track_id
---> 35     self.on_track_id_change()
        self = <napari_arboretum.visualisation.matplotlib_plotter.MPLPropertyPlotter object at 0x000002AF88EF18B0>

File ~\mambaforge\envs\laptrack_env2\lib\site-packages\napari_arboretum\visualisation\base_plotter.py:153, in PropertyPlotterBase.on_track_id_change(self=<napari_arboretum.visualisation.matplotlib_plotter.MPLPropertyPlotter object>)
    152 def on_track_id_change(self) -> None:
--> 153     self.plot_property()
        self = <napari_arboretum.visualisation.matplotlib_plotter.MPLPropertyPlotter object at 0x000002AF88EF18B0>

File ~\mambaforge\envs\laptrack_env2\lib\site-packages\napari_arboretum\visualisation\base_plotter.py:161, in PropertyPlotterBase.plot_property(self=<napari_arboretum.visualisation.matplotlib_plotter.MPLPropertyPlotter object>)
    155 def plot_property(self) -> None:
    156     """
    157     Plot a property. The property is taken from the currently selected
    158     layer, currently selected track_id, and the property used to 'color_by'
    159     in the napari viewer.
    160     """
--> 161     t, prop = self.get_track_properties()
        self = <napari_arboretum.visualisation.matplotlib_plotter.MPLPropertyPlotter object at 0x000002AF88EF18B0>
    163     self.clear()
    164     self.plot(t, prop)

File ~\mambaforge\envs\laptrack_env2\lib\site-packages\napari_arboretum\visualisation\base_plotter.py:184, in PropertyPlotterBase.get_track_properties(self=<napari_arboretum.visualisation.matplotlib_plotter.MPLPropertyPlotter object>)
    182 all_props = pd.DataFrame(self.tracks.properties)
    183 all_props = all_props.loc[all_props["track_id"] == self.track_id]
--> 184 return all_props["t"].values, all_props[self.tracks.color_by].values
        all_props =      track_id
6           1
7           1
8           1
9           1
53          1
54          1
55          1
56          1
57          1
105         1
106         1
107         1
108         1
109         1
153         1
154         1
155         1
156         1
157         1
199         1
200         1
201         1
202         1
203         1
247         1
248         1
249         1
250         1
251         1
295         1
296         1
297         1
298         1
299         1
343         1
344         1
345         1
346         1
347         1
389         1
390         1
391         1
392         1
393         1
404         1
405         1
406         1
445         1
        self = <napari_arboretum.visualisation.matplotlib_plotter.MPLPropertyPlotter object at 0x000002AF88EF18B0>

File ~\mambaforge\envs\laptrack_env2\lib\site-packages\pandas\core\frame.py:3807, in DataFrame.__getitem__(self=     track_id
6           1
7           1
8     ...      1
405         1
406         1
445         1, key='t')
   3805 if self.columns.nlevels > 1:
   3806     return self._getitem_multilevel(key)
-> 3807 indexer = self.columns.get_loc(key)
        key = 't'
        indexer = None
        self =      track_id
6           1
7           1
8           1
9           1
53          1
54          1
55          1
56          1
57          1
105         1
106         1
107         1
108         1
109         1
153         1
154         1
155         1
156         1
157         1
199         1
200         1
201         1
202         1
203         1
247         1
248         1
249         1
250         1
251         1
295         1
296         1
297         1
298         1
299         1
343         1
344         1
345         1
346         1
347         1
389         1
390         1
391         1
392         1
393         1
404         1
405         1
406         1
445         1
   3808 if is_integer(indexer):
   3809     indexer = [indexer]

File ~\mambaforge\envs\laptrack_env2\lib\site-packages\pandas\core\indexes\base.py:3804, in Index.get_loc(self=Index(['track_id'], dtype='object'), key='t', method=None, tolerance=None)
   3802     return self._engine.get_loc(casted_key)
   3803 except KeyError as err:
-> 3804     raise KeyError(key) from err
        key = 't'
   3805 except TypeError:
   3806     # If we have a listlike key, _check_indexing_error will raise
   3807     #  InvalidIndexError. Otherwise we fall through and re-raise
   3808     #  the TypeError.
   3809     self._check_indexing_error(key)

KeyError: 't'
yfukai commented

It would be exciting if we would have the lineage tree visualization with Arboretum! Let me find the cause when I have time.