From 8b83a88225e0980f51948903f4eba5ad9806eb53 Mon Sep 17 00:00:00 2001 From: maurerv Date: Tue, 9 Jan 2024 10:40:00 +0100 Subject: [PATCH] Added napari integration to docs --- colabseg/napari_integration.py | 107 ++++++++++++++++++++++++-- colabseg/segmentation_gui.py | 4 + doc/_templates/autosummary/README.txt | 33 ++++++++ doc/_templates/autosummary/class.rst | 44 +++++------ doc/conf.py | 5 +- doc/reference/helpers.rst | 19 ++++- 6 files changed, 181 insertions(+), 31 deletions(-) create mode 100644 doc/_templates/autosummary/README.txt diff --git a/colabseg/napari_integration.py b/colabseg/napari_integration.py index f457dd0..7145319 100644 --- a/colabseg/napari_integration.py +++ b/colabseg/napari_integration.py @@ -1,3 +1,5 @@ +from typing import List, Dict + import threading import numpy as np import napari @@ -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 @@ -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( @@ -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] @@ -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() @@ -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: @@ -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() diff --git a/colabseg/segmentation_gui.py b/colabseg/segmentation_gui.py index b6e36a6..0081ebf 100644 --- a/colabseg/segmentation_gui.py +++ b/colabseg/segmentation_gui.py @@ -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 = { @@ -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( diff --git a/doc/_templates/autosummary/README.txt b/doc/_templates/autosummary/README.txt new file mode 100644 index 0000000..67d9a20 --- /dev/null +++ b/doc/_templates/autosummary/README.txt @@ -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. diff --git a/doc/_templates/autosummary/class.rst b/doc/_templates/autosummary/class.rst index 77acfb0..a9c6304 100644 --- a/doc/_templates/autosummary/class.rst +++ b/doc/_templates/autosummary/class.rst @@ -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 %} \ No newline at end of file diff --git a/doc/conf.py b/doc/conf.py index bf02884..a279a43 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -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"] diff --git a/doc/reference/helpers.rst b/doc/reference/helpers.rst index 9b1658f..81b8598 100644 --- a/doc/reference/helpers.rst +++ b/doc/reference/helpers.rst @@ -4,4 +4,21 @@ Helpers .. automodule:: colabseg.utilities :members: :undoc-members: - :show-inheritance: \ No newline at end of file + :show-inheritance: + + +.. currentmodule:: colabseg.napari_integration + +ColabSegNapariWidget +~~~~~~~~~~~~~~~~~~~~ +.. autosummary:: + :toctree: api/ + + ColabSegNapariWidget + +NapariManager +~~~~~~~~~~~~~ +.. autosummary:: + :toctree: api/ + + NapariManager \ No newline at end of file