Skip to content

Commit

Permalink
Added napari integration to docs
Browse files Browse the repository at this point in the history
  • Loading branch information
maurerv committed Jan 9, 2024
1 parent 2416d24 commit 8b83a88
Show file tree
Hide file tree
Showing 6 changed files with 181 additions and 31 deletions.
107 changes: 102 additions & 5 deletions colabseg/napari_integration.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from typing import List, Dict

import threading
import numpy as np
import napari
Expand All @@ -11,7 +13,28 @@


class ColabSegNapariWidget(widgets.Container):
def __init__(self, viewer, colabsegdata_instance: type) -> "ColabSegNapariWidget":
"""
A widget for napari viewer to manage and visualize data from ColabSegData instances.
Parameters
----------
viewer : napari.Viewer
The napari viewer instance to which this widget will be added.
colabsegdata_instance : :py:class:`colabseg.new_gui_functions.ColabSegData`, optional
An instance of ColabSegData containing the data to be visualized.
Attributes
----------
viewer : napari.Viewer
The napari viewer instance.
pixel_size : float
Pixel size of the data, obtained from colabsegdata_instance.
dropdown : magicgui.widgets.ComboBox
Dropdown menu for selecting clusters to highlight.
_previous_selection : str or None
The previously selected cluster name.
"""
def __init__(self, viewer, colabsegdata_instance: "ColabSegData") -> "ColabSegNapariWidget":
super().__init__(layout="vertical")

self.viewer = viewer
Expand All @@ -31,12 +54,32 @@ def __init__(self, viewer, colabsegdata_instance: type) -> "ColabSegNapariWidget

self.append(self.dropdown)

def _get_point_layers(self) -> None:
def _get_point_layers(self) -> List[str]:
"""
Returns a sorted list of point layer names from the napari viewer.
Returns
-------
list
A sorted list of strings of point layers present in the viewer.
"""
return sorted(
[layer.name for layer in self.viewer.layers if isinstance(layer, Points)]
)

def load_data(self, data: type) -> None:
def load_data(self, data: "ColabSegData") -> None:
"""
Loads and visualizes data from a given ColabSegData instance. Points are
divided by the pixel_size attribute of the ColabSegData instance. Currently,
the attributes cluster_list_tv, cluster_list_fits, protein_positions_list
from the ColabSegData instance are visualized as point clouds.
Parameters
----------
data : type
An instance of ColabSegData containing the data to be visualized.
"""

for index, cluster in enumerate(data.cluster_list_tv):
scaled_points = np.divide(cluster, self.pixel_size)
self.viewer.add_points(
Expand Down Expand Up @@ -71,6 +114,14 @@ def load_data(self, data: type) -> None:
)

def _on_cluster_selected(self, event) -> None:
"""
Callback function for dropdown selection changes. Highlights the selected cluster.
Parameters
----------
event
The event triggered on changing the dropdown selection.
"""
if self.dropdown.value is None:
for point_cloud in self._get_point_layers():
selected_layer = self.viewer.layers[point_cloud]
Expand All @@ -86,7 +137,18 @@ def _on_cluster_selected(self, event) -> None:

self._previous_selection = self.dropdown.value

def export_data(self) -> {str : [np.ndarray]}:
def export_data(self) -> Dict[str, List[np.ndarray]]:
"""
Exports the point cloud data currently visualized in the napari viewer.
The order of the output lists correspond to the input order. Deleted
point clouds will be represented as empty lists.
Returns
-------
dict of {str : list of numpy.ndarray}
A dictionary where keys are point cloud class names and values
are lists of numpy arrays representing point cloud coordinates.
"""
ret = {}

point_clouds = self._get_point_layers()
Expand All @@ -105,7 +167,24 @@ def export_data(self) -> {str : [np.ndarray]}:


class NapariManager:
def __init__(self, display_data=None, colabsegdata_instance=None, **kwargs):
"""
Manages the napari viewer and associated widgets for data visualization and interaction.
Parameters
----------
display_data : array-like, optional
Data to be displayed in the napari viewer, typically image data.
colabsegdata_instance : :py:class:`colabseg.new_gui_functions.ColabSegData`, optional
An instance of ColabSegData for data visualization.
Attributes
----------
viewer : napari.Viewer
The napari viewer instance.
colabseg_widget : :py:class:`ColabSegNapariWidget`
The widget for managing and visualizing ColabSegData instances.
"""
def __init__(self, display_data : np.ndarray=None, colabsegdata_instance : "ColabSegData" = None, **kwargs):
self.viewer = napari.Viewer()

if display_data is not None:
Expand All @@ -122,10 +201,28 @@ def __init__(self, display_data=None, colabsegdata_instance=None, **kwargs):
)

def run(self):
"""
Launches the napari viewer.
"""
napari.run()

def close(self):
"""
Closes the napari viewer.
"""
self.viewer.close()

def export_data(self):
"""
Exports the point cloud data from the colabseg_widget.
Returns
-------
dict
The point cloud data exported from the colabseg_widget.
See Also
--------
:py:meth:`ColabSegNapariWidget.export_data`
"""
return self.colabseg_widget.export_data()
4 changes: 4 additions & 0 deletions colabseg/segmentation_gui.py
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,9 @@ def open_napari_wrapper(change):
self.all_widgets["load_raw_image_button"].on_click(open_napari_wrapper)

def sync_napari_wrapper(change):
if self.napari_manager is None:
return None

data = self.napari_manager.export_data()

mapping = {
Expand All @@ -303,6 +306,7 @@ def sync_napari_wrapper(change):

if self.napari_manager is not None:
self.napari_manager.close()
self.napari_manager = None
self.reload_gui()

self.all_widgets["save_raw_image_button"] = widgets.Button(
Expand Down
33 changes: 33 additions & 0 deletions doc/_templates/autosummary/README.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
class.rst is largely taken from the pandas python library (https://github.com/pandas-dev/pandas/blob/main/doc/source/_static/), which operates under the following license:

BSD 3-Clause License

Copyright (c) 2008-2011, AQR Capital Management, LLC, Lambda Foundry, Inc. and PyData Development Team
All rights reserved.

Copyright (c) 2011-2023, Open source contributors.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.

* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.

* Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
44 changes: 21 additions & 23 deletions doc/_templates/autosummary/class.rst
Original file line number Diff line number Diff line change
@@ -1,36 +1,34 @@
{% extends "!autosummary/class.rst" %}
{{ objname | escape | underline}}

{{ objname | escape }}
{{ underline }}
.. currentmodule:: {{ module }}

.. .. currentmodule:: {{ module }}
.. autoclass:: {{ objname }}

.. .. autoclass:: {{ objname }}
{% block methods %}

{% block methods %}
{% if methods %}
{% block attributes %}
{% if attributes %}
.. rubric:: {{ _('Attributes') }}

.. autosummary::
:toctree:
{% for item in all_methods %}
{%- if not item.startswith('_') or item in ['__call__'] %}
{{ name }}.{{ item }}
{%- endif -%}
{% for item in attributes %}
{% if item in members and not item.startswith('_') %}
{{ name }}.{{ item }}
{% endif %}
{%- endfor %}
{% endif %}
{% endblock %}

{% endif %}
{% endblock %}

{% block attributes %}
{% if attributes %}
{% if methods %}
.. rubric:: {{ _('Methods') }}

.. autosummary::
:toctree:
{% for item in all_attributes %}
{%- if not item.startswith('_') %}
{{ name }}.{{ item }}
{%- endif -%}
{% for item in methods %}
{% if item in members and (not item.startswith('_') or item in ['__call__']) %}
{{ name }}.{{ item }}
{% endif %}
{%- endfor %}

{% endif %}
{% endblock %}
{% endif %}
{% endblock %}
5 changes: 3 additions & 2 deletions doc/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,12 @@
autosummary_imported_members = True
add_module_names = False

autodoc_typehints_format = "short"
numpydoc_show_class_members = False
numpydoc_show_inherited_class_members = False

autodoc_typehints_format = "short"
autodoc_typehints = "none"


templates_path = ["_templates"]
exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"]

Expand Down
19 changes: 18 additions & 1 deletion doc/reference/helpers.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,21 @@ Helpers
.. automodule:: colabseg.utilities
:members:
:undoc-members:
:show-inheritance:
:show-inheritance:


.. currentmodule:: colabseg.napari_integration

ColabSegNapariWidget
~~~~~~~~~~~~~~~~~~~~
.. autosummary::
:toctree: api/

ColabSegNapariWidget

NapariManager
~~~~~~~~~~~~~
.. autosummary::
:toctree: api/

NapariManager

0 comments on commit 8b83a88

Please sign in to comment.