Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

docs: Add docs for plugin create #795

Open
wants to merge 13 commits into
base: develop
Choose a base branch
from
6 changes: 3 additions & 3 deletions .gitpod.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
tasks:
- init: >
pip install -e .[test,pyqt]
pip install -r docs/requirements.txt
- before: sudo apt-get update && sudo apt-get install -y herbstluftwm libxkbcommon-x11-0 libxcb-icccm4 libxcb-image0 libxcb-keysyms1 libxcb-randr0 libxcb-render-util0 libxcb-xinerama0 libxcb-xfixes0 x11-utils xvfb
# command: Xvfb :99 -screen 0 640x480x8 -nolisten tcp & herbstluftwm
init: pip install -e .[all,test,docs] && bash ./build_utils/download_data.sh
1 change: 1 addition & 0 deletions docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
"sphinx_autodoc_typehints",
"PartSegCore.sphinx.auto_parameters",
"sphinxcontrib.autodoc_pydantic",
"sphinx.ext.autosectionlabel",
]
Comment on lines 33 to 37
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The addition of "sphinx.ext.autosectionlabel" to the extensions list is correct and aligns with the PR objective of enhancing documentation. Ensure that if there are any documents with duplicate section names, the configuration option "autosectionlabel_prefix_document" is set to True to avoid conflicts.


# Add any paths that contain templates here, relative to this directory.
Expand Down
1 change: 1 addition & 0 deletions docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ The documentation is incomplete. Many utilities are undocumented.
interface-overview/interface-overview
state_store
error_reporting
plugins_creation


Indices and tables
Expand Down
116 changes: 116 additions & 0 deletions docs/plugins_creation.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
Creating plugins for PartSeg
============================

PartSeg has a plugin system, but because of a lack of documentation, it is not very popular.
This document is an attempt to explain how to create plugins for PartSeg. The list of good plugins that could be inspirration
is available at the :ref:`end of this document<Already existing plugins>`.
Comment on lines +4 to +6
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor grammatical error.

- This document is an attempt to explain how to create plugins for PartSeg. The list of good plugins that could be inspirration
+ This document is an attempt to explain how to create plugins for PartSeg. The list of good plugins that could be inspiration

Committable suggestion

IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation.

Suggested change
PartSeg has a plugin system, but because of a lack of documentation, it is not very popular.
This document is an attempt to explain how to create plugins for PartSeg. The list of good plugins that could be inspirration
is available at the :ref:`end of this document<Already existing plugins>`.
PartSeg has a plugin system, but because of a lack of documentation, it is not very popular.
This document is an attempt to explain how to create plugins for PartSeg. The list of good plugins that could be inspiration
is available at the :ref:`end of this document<Already existing plugins>`.


.. note::

This document needs to be completed and may need to be corrected. If you have any questions, please inform me by a GitHub issue.

PartSeg plugin system was designed at the beginning of the project when PartSeg was a python 2.7 project, and still, not
all possibilities given by recent python versions are used. For example, plugin methods are still class-based,
but there is a plan to allow function-based plugins.

Where plugin could contribute in PartSeg
----------------------------------------

Here I describe nine areas where plugins could contribute:


* **Threshold algorithms** - algorithms for binarizing single channel data.

* **Noise Filtering algorithms** - algorithms for filtering noise from a single channel of data.

* **Flow algorithms** - Watershed-like algorithms for flow from core object to the whole region.
Currently, they are used in "Lower Threshold with Watershed" and "Upper Threshold with Watershed"
segmentation methods

* **ROI Analysis algorithm** - algorithm available in *ROI Analysis* GUI for ROI extraction.

* **ROI Analysis load method** - method to load data in *ROI Analysis* GUI.

* **ROI Analysis save method** - method to save data in *ROI Analysis* GUI.

* **ROI Mask algorithm** - algorithm available in *ROI Mask* GUI for ROI extraction.

* **ROI Mask load method** - method to load data in *ROI Mask* GUI.

* **ROI Mask save method** - method to save data in *ROI Mask* GUI.


A person interested in developing a plugin could wound the whole list in :py:mod:`PartSegCore.register` module.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Possible typo or incorrect word usage.

- A person interested in developing a plugin could wound the whole list in :py:mod:`PartSegCore.register` module.
+ A person interested in developing a plugin could find the whole list in :py:mod:`PartSegCore.register` module.

Committable suggestion

IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation.

Suggested change
A person interested in developing a plugin could wound the whole list in :py:mod:`PartSegCore.register` module.
A person interested in developing a plugin could find the whole list in :py:mod:`PartSegCore.register` module.


Plugin detection
----------------

PartSeg uses :py:func:`pkg_resources.iter_entry_points` module to detect plugins.
To be detected, the plugin needs to provide one of ``partseg.plugins``, ``PartSeg.plugins``.
``partsegcore.plugins`` or ``PartSegCore.plugins`` entry point in python package metadata.
The example of ``setup.cfg`` configuration from a plugin could be found `here <https://github.com/Czaki/Trapalyzer/blob/fc5b84fde2fb1fe4bea75bdd1e4a483772115500/setup.cfg#L43>`_




``--develop`` mode of PartSeg
-----------------------------

PartSeg allows to use of plugins in ``--develop`` mode. In this mode is a settings dialog the additional
tab "Develop" is added. In this tab, there is Reload button. After the button press, PartSeg tries
to reload all plugins. This feature allows the development of a plugin without the need of too often restarting of PartSeg.

This approach is limited and reloads only entries pointed in entry points.
It is the plugin creator's responsibility to reload all other modules required by the plugin.
To detect if the file is during reload, the plugin author could use the following code:


.. code-block:: python

try:

reloading
except NameError:
reloading = False
else:
reloading = True

The ``importlib.reload`` function could be used to reload required modules.

.. code-block:: python

import importlib
importlib.reload(module)


Already existing plugins
------------------------
Here we list already existing PartSeg plugins. All plugins are also available when using PartSeg as a napari plugin.
New plugin creator may want to look at them to see how to create new plugins.

PartSeg-smfish
~~~~~~~~~~~~~~
This is plugin for processing smFISH data. It could be found under https://github.com/4DNucleome/PartSeg-smfish/ page.
This plugin provides a custom segmentation algorithm for smfish data (inspired by bigFISH algorithm that does not work well for our data)
and custom measurement methods.

The plugin is available from pypi and conda.

PartSeg-bioimageio
~~~~~~~~~~~~~~~~~~
PartSeg plugin to run bioimage.io deep learning models. It could be found under https:/github.com/czaki/PartSeg-bioimageio/ page
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The URL provided seems to be missing a colon, which could lead to a broken link.

- PartSeg plugin to run bioimage.io deep learning models. It could be found under https:/github.com/czaki/PartSeg-bioimageio/ page
+ PartSeg plugin to run bioimage.io deep learning models. It could be found under https://github.com/czaki/PartSeg-bioimageio/ page

Committable suggestion

IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation.

Suggested change
PartSeg plugin to run bioimage.io deep learning models. It could be found under https:/github.com/czaki/PartSeg-bioimageio/ page
PartSeg plugin to run bioimage.io deep learning models. It could be found under https://github.com/czaki/PartSeg-bioimageio/ page

This plugin allows to selection model saved in bioimage.io format from the disc and runs it on selected data with the test this model in interactive mode.

As it depends on deep learn libraries, it cannot be used in PartSeg binary distribution.

In this plugin, plugin creator could see an example of using custom magicgui-based widget for selecting a model.

The plugin is under active development and currently available only on GitHub.

Trapalyzer
~~~~~~~~~~

This is plugin developed for process neutrophile `data <https://zenodo.org/record/7335168>`_.
It provides custom segmentation data to find multiple class of cells in one run and custom measurement methods.

It could be found on github https://github.com/Czaki/Trapalyzer and pypi.
38 changes: 24 additions & 14 deletions package/PartSegCore/algorithm_describe_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -130,22 +130,32 @@ def __get__(self, obj, klass):
return model


def _partial_abstractmethod(funcobj):
funcobj.__is_partial_abstractmethod__ = True
return funcobj
def _partial_abstractmethod(func_obj):
func_obj.__is_partial_abstractmethod__ = True
return func_obj


class AlgorithmDescribeBaseMeta(ABCMeta):
def __new__(cls, name, bases, attrs, **kwargs):
cls2 = super().__new__(cls, name, bases, attrs, **kwargs)
__argument_class__: typing.Optional[typing.Type[PydanticBaseModel]] = None

def get_fields(self) -> typing.List[typing.Union[AlgorithmProperty, str]]:
"""
This function return list of parameters needed by algorithm. It is used for generate form in User Interface

:return: list of algorithm parameters and comments
"""
raise NotImplementedError

def __new__(mcs, name, bases, attrs, **kwargs):
cls = super().__new__(mcs, name, bases, attrs, **kwargs)
if (
not inspect.isabstract(cls2)
and hasattr(cls2.get_fields, "__is_partial_abstractmethod__")
and cls2.__argument_class__ is None
not inspect.isabstract(cls)
and hasattr(cls.get_fields, "__is_partial_abstractmethod__")
and cls.__argument_class__ is None
):
raise RuntimeError("class need to have __argument_class__ set or get_fields functions defined")
cls2.__new_style__ = getattr(cls2.get_fields, "__is_partial_abstractmethod__", False)
return cls2
cls.__new_style__ = getattr(cls.get_fields, "__is_partial_abstractmethod__", False)
return cls
Comment on lines +149 to +158
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ensure correct attribute access in the __new__ method of AlgorithmDescribeBaseMeta. Use hasattr(cls, 'get_fields') and hasattr(cls.get_fields, '__is_partial_abstractmethod__') to check the attribute on the method.

- and hasattr(cls.get_fields, "__is_partial_abstractmethod__")
+ and hasattr(cls, 'get_fields') and hasattr(cls.get_fields, '__is_partial_abstractmethod__')

Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation.

Suggested change
def __new__(mcs, name, bases, attrs, **kwargs):
cls = super().__new__(mcs, name, bases, attrs, **kwargs)
if (
not inspect.isabstract(cls2)
and hasattr(cls2.get_fields, "__is_partial_abstractmethod__")
and cls2.__argument_class__ is None
not inspect.isabstract(cls)
and hasattr(cls.get_fields, "__is_partial_abstractmethod__")
and cls.__argument_class__ is None
):
raise RuntimeError("class need to have __argument_class__ set or get_fields functions defined")
cls2.__new_style__ = getattr(cls2.get_fields, "__is_partial_abstractmethod__", False)
return cls2
cls.__new_style__ = getattr(cls.get_fields, "__is_partial_abstractmethod__", False)
return cls
def __new__(mcs, name, bases, attrs, **kwargs):
cls = super().__new__(mcs, name, bases, attrs, **kwargs)
if (
not inspect.isabstract(cls)
and hasattr(cls, 'get_fields') and hasattr(cls.get_fields, '__is_partial_abstractmethod__')
and cls.__argument_class__ is None
):
raise RuntimeError("class need to have __argument_class__ set or get_fields functions defined")
cls.__new_style__ = getattr(cls.get_fields, "__is_partial_abstractmethod__", False)
return cls



class AlgorithmDescribeBase(ABC, metaclass=AlgorithmDescribeBaseMeta):
Expand Down Expand Up @@ -253,7 +263,7 @@ def __init__(self, *args: AlgorithmType, class_methods=None, methods=None, sugge
"""
:param class_methods: list of method which should be class method
:param methods: list of method which should be instance method
:param kwargs: elements passed to OrderedDict constructor (may be initial elements). I suggest to not use this.
:param kwargs: elements passed to OrderedDict constructor (maybe initial elements). I suggest to not use this.
"""
super().__init__(**kwargs)
self.suggested_base_class = suggested_base_class
Expand Down Expand Up @@ -466,15 +476,15 @@ def __new__(cls, name, bases, attrs, **kwargs):

def allow_positional_args(func):
@wraps(func)
def _wraps(self, *args, **kwargs):
def _wraps(self, *args, **kwargs_):
if args:
warnings.warn(
"Positional arguments are deprecated, use keyword arguments instead",
FutureWarning,
stacklevel=2,
)
kwargs.update(dict(zip(self.__fields__, args)))
return func(self, **kwargs)
kwargs_.update(dict(zip(self.__fields__, args)))
return func(self, **kwargs_)

return _wraps

Expand Down
14 changes: 11 additions & 3 deletions package/PartSegCore/register.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,11 +44,19 @@ class RegisterEnum(Enum):
flow = 0 #: algorithm for calculation flow from core object to borders. For spiting touching objects, deprecated
threshold = 1 #: threshold algorithms. From greyscale array to binary array
noise_filtering = 2 #: filter noise from image
analysis_algorithm = 3 #: algorithm for creating segmentation in analysis PartSeg part
mask_algorithm = 4 #: algorithm for creating segmentation in mask PartSeg part
analysis_algorithm = 3
"""
algorithm for creating segmentation in analysis PartSeg part
(deprecated in favour of roi_analysis_segmentation_algorithm)
"""
mask_algorithm = 4
"""
algorithm for creating segmentation in mask PartSeg part
(deprecated in favour of roi_mask_segmentation_algorithm)
"""
analysis_save = 5 #: save functions for analysis part
analysis_load = 6 #: load functions for analysis part
mask_load = 7 #: load functions for mask part
mask_load = 7 #: load functions for mask part)
image_transform = 8 #: transform image, like interpolation
mask_save_parameters = 9 #: save metadata for mask part (currently creating json file)
mask_save_components = 10 #: save each segmentation component in separate file. Save location is directory
Comment on lines 44 to 62
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 NOTE
Codebase verification is a beta feature.

The deprecation of analysis_algorithm and mask_algorithm is noted, but references to these deprecated enums are still present in the code. The following locations should be reviewed and updated to use the new roi_analysis_segmentation_algorithm and roi_mask_segmentation_algorithm:

  • ./package/PartSegCore/register.py: lines 77, 97 (for analysis_algorithm)
  • ./package/PartSegCore/register.py: lines 78, 98 (for mask_algorithm)

Expand Down