feat: init
@@ -0,0 +1,172 @@
|
||||
.. _contributing_guidelines:
|
||||
|
||||
************************
|
||||
Contributing Guidelines
|
||||
************************
|
||||
|
||||
.. contents:: Table of Contents
|
||||
:depth: 3
|
||||
|
||||
General Overview
|
||||
=======================
|
||||
|
||||
We welcome contributions in the form of bug reports, bug fixes, new features and documentation. Some best practices for contributing code are outlined below. If you are considering refactoring the code, please reach out to us prior to starting this process.
|
||||
|
||||
-------------------
|
||||
|
||||
Project Philosophy
|
||||
==================
|
||||
|
||||
**navigate** is designed with the following principles in mind:
|
||||
|
||||
* Prioritize standard library imports for maximum stability, and minimize external dependencies.
|
||||
* Abstraction layer to drive different camera types, etc.
|
||||
* Plugin architecture for extensibility.
|
||||
* Maximize productivity for biological users through robust graphical user interface-based workflows.
|
||||
* Performant and responsive.
|
||||
* Brutally obvious, well-documented, clean code organized in an industry standard Model-View-Controller architecture.
|
||||
|
||||
We ask that all contributions adhere to these principles.
|
||||
|
||||
-------------------
|
||||
|
||||
General Principles
|
||||
==================
|
||||
|
||||
- We use a `model-view-controller architecture <https://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller>`_. New functionality should keep this strong separation. More information can be found in the :ref:`software architecture <software-architecture-section>` section.
|
||||
- Please do not create new configuration variables unless absolutely necessary, especially in the ``configuration.yaml`` and ``experiment.yaml`` files. A new variable is necessary only if no variable stores similar information or there is no way to use the most similar variable without disrupting another part of the code base.
|
||||
- We are happy to discuss code refactors for improved clarity and speed. However, please do not modify something that is already working without discussing this with the software team in advance.
|
||||
- All code that modifies microscope control behavior must be reviewed and tested on a live system prior to merging into the ``develop`` branch.
|
||||
- Scientific Units - Please express quantities in the following units when they are in the standard model/view/controller code. Deviations from this can occur where it is necessary to pass a different unit to a piece of hardware.
|
||||
|
||||
* Time - Milliseconds
|
||||
* Distance - Micrometers
|
||||
* Voltage - Volts
|
||||
* Rotation - Degrees
|
||||
|
||||
|
||||
-------------------
|
||||
|
||||
Getting Started
|
||||
==============
|
||||
|
||||
1. **Fork and Clone**: Fork the repository and clone it locally
|
||||
2. **Set Up Environment**: `pip install -e .[dev]` to install in development mode
|
||||
3. **Install Pre-commit Hooks**: `pre-commit install` to set up linting hooks
|
||||
4. **Run Tests**: Ensure `pytest` passes before making changes
|
||||
|
||||
-------------------
|
||||
|
||||
Pull Request Process
|
||||
===================
|
||||
|
||||
1. Create a branch from `develop` with a descriptive name
|
||||
2. Make your changes following the guidelines in this document
|
||||
3. Add tests for new functionality
|
||||
4. Ensure all tests pass and linting is clean
|
||||
5. Update documentation as needed
|
||||
6. Submit PR to the `develop` branch with clear description of changes
|
||||
|
||||
|
||||
-------------------
|
||||
|
||||
Coding Style
|
||||
============
|
||||
|
||||
Naming Conventions
|
||||
------------------
|
||||
We follow the `PEP8 code style guide <https://peps.python.org/pep-0008/>`_. All class names are written in ``CamelCase`` and all variable names are ``lowercase_and_separated_by_underscores``.
|
||||
|
||||
Type Hints
|
||||
----------------
|
||||
Type hints are used throughout the code base. Please add type hints to any new methods or functions you create. If you are unsure how to do this, please see `PEP 484 <https://peps.python.org/pep-0484/>`_ for more information.
|
||||
|
||||
Numpydoc
|
||||
----------------
|
||||
We use `Numpydoc <https://numpydoc.readthedocs.io/en/latest/format.html>`_ style docstrings throughout the code base. Please use this style for any new methods or functions you create.
|
||||
|
||||
Sphinx
|
||||
----------------
|
||||
We use `Sphinx <https://www.sphinx-doc.org/en/master/>`_ to generate documentation from documented methods, attributes, and classes. Please document all new methods, attributes, and classes using a Sphinx compatible version of `Numpydoc <https://www.sphinx-doc.org/en/master/usage/extensions/example_numpy.html>`_.
|
||||
|
||||
Linters
|
||||
----------------
|
||||
We use `Ruff <https://docs.astral.sh/ruff/>`_ to enforce consistent code formatting. Please run Ruff on your code before making a pull request. Ideally, these actions should be integrated as part of a pre-commit hook (see below).
|
||||
|
||||
Pre-commit Hooks
|
||||
----------------
|
||||
We use `pre-commit hooks <https://pre-commit.com/>`_ to enforce consistent code formatting and automate some of the code review process. In some rare cases, the linter may complain about a line of code that is actually fine. For example, in the example code below, Ruff linter complains that the start_stage class is imported but not used. However, it is actually used in as part of an ``exec`` statement.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from navigate.model.device_startup_functions import start_stage
|
||||
device_name = stage
|
||||
exec(f"self.{device_name} = start_{device_name}(name, device_connection, configuration, i, is_synthetic)")
|
||||
|
||||
To avoid this error, add a ``# noqa`` comment to the end of the line to tell Ruff to ignore the error.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from navigate.model.device_startup_functions import start_stage # noqa
|
||||
|
||||
Unit Tests
|
||||
----------------
|
||||
Each line of code is unit tested to ensure it behaves appropriately and alert future coders to modifications that break expected functionality. Guidelines for writing good unit tests can be found `here <https://stackoverflow.com/questions/61400/what-makes-a-good-unit-test>`_ and `over here <https://medium.com/chris-nielsen/so-whats-a-good-unit-test-look-like-71f750333ac0>`_, or in examples of unit tests in this repository's ``test`` folder. We use the `pytest library <https://docs.pytest.org/en/7.2.x/>`_ to evaluate unit tests. Please check that unit tests pass on your machine before making a pull request.
|
||||
|
||||
Dictionary Parsing
|
||||
------------------
|
||||
The :ref:`configuration file <configuration_file>` is loaded as a large dictionary object, and it is easy to create small errors in the dictionary that can crash the program. To avoid this, when getting properties from the configuration dictionary, it is best to use the ``.get()`` command, which provides you with the opportunity to also have a default value should the key provided not be found. For example,
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# Galvo Waveform Information
|
||||
self.galvo_waveform = self.device_config.get("waveform", "sawtooth")
|
||||
|
||||
Here, we try to retrieve the ``waveform`` key from a the ``self.device_config`` dictionary. In the case that this key is not available, it then by default returns ``sawtooth``. If however the ``waveform`` key is found, it will provide the value associated with it.
|
||||
|
||||
-------------------
|
||||
|
||||
Communicating with Hardware
|
||||
===========================
|
||||
|
||||
Threads and Blocking
|
||||
---------------------------
|
||||
In handling hardware devices, such as Sutter's MP-285A stage, using threads can introduce complexities, especially when simultaneous read and write operations occur over a shared resource like a serial line. An encountered issue demonstrated the challenges when two different threads attempted to write to and read from the same serial port simultaneously. This action led to data corruption due to interleaving of read/write calls that require precise handshaking, characteristic of the MP-285A's communication protocol. The solution involved implementing a blocking mechanism using `threading.Event()` to ensure that operations on the serial port do not overlap, showcasing the difficulties of multithreading sequential processes. To mitigate such issues, a design where each hardware device operates within its own dedicated thread is advisable. This approach simplifies the management of device communications by enforcing sequential execution, eliminating the need to handle complex concurrency issues inherent in multithreading environments. This strategy ensures robust and error-free interaction with hardware devices.
|
||||
|
||||
|
||||
Dedicated Device Interfaces
|
||||
---------------------------
|
||||
|
||||
**navigate** implements a robust hardware abstraction layer through dedicated device interfaces. When integrating new hardware devices:
|
||||
|
||||
* Each hardware device type (cameras, stages, etc.) has its own dedicated interface that must be implemented
|
||||
* All hardware classes inherit from a base class specific to the device type
|
||||
* Base classes include AbstractMethods that define the required interface for any derived hardware class
|
||||
* These abstract methods clearly communicate which functions must be implemented for any new hardware
|
||||
* Failure to override these abstract methods in derived classes will result in runtime errors
|
||||
|
||||
This architecture ensures consistency across different hardware implementations while providing clear guidance for developers adding support for new devices. When adding support for a new hardware device, first identify the appropriate base class and ensure you implement all required abstract methods.
|
||||
|
||||
|
||||
|
||||
|
||||
.. Developing with a Mac
|
||||
.. =====================
|
||||
|
||||
.. Many of us have Apple products and use them for development. However, there are some issues that you may encounter when developing on a Mac. Below are some of the issues we have encountered and how to resolve them.
|
||||
|
||||
.. .. code-block:: console
|
||||
|
||||
.. OSError: You tried to simultaneously open more SharedNDArrays than are allowed by your system!
|
||||
|
||||
.. This results from a limitation in the number of shared memory objects that can be created on a Mac. To figure out how many objects can open, open a terminal and run the following command
|
||||
|
||||
.. .. code-block:: console
|
||||
|
||||
.. ulimit -n
|
||||
|
||||
.. To increase this number, simply add an integer value after it. In our hands, 1000 typically works:
|
||||
|
||||
.. .. code-block:: console
|
||||
|
||||
.. ulimit -n 1000
|
||||
@@ -0,0 +1,109 @@
|
||||
Developer Install
|
||||
=================
|
||||
|
||||
The following sections provide detailed guidance for developers that wish to contribute to **navigate**. They aim to help you set up your development environment, effectively work with the codebase, and contribute to its growth.
|
||||
|
||||
* :ref:`Download and Install Git <download_git>`
|
||||
* :ref:`Create Directory for Installation <create_directory>`
|
||||
* :ref:`Fork Repository <fork_repository>`
|
||||
* :ref:`Clone Repository <clone_repository>`
|
||||
* :ref:`Install navigate in a Virtual Environment <virtual_environment>`
|
||||
|
||||
|
||||
.. _download_git:
|
||||
|
||||
**Download Git**
|
||||
|
||||
If you do not have `Git already installed <https://git-scm.com/downloads>`_, you will need to do so before cloning the repo. We also recommend installing `GitHub Desktop <https://github.com/apps/desktop>`_ for a more user-friendly experience.
|
||||
|
||||
.. _create_directory:
|
||||
|
||||
**Create a directory where the repository will be cloned**
|
||||
|
||||
We recommend a path/location that is easy to find and access such as the your Desktop or Documents. Once the folder is created, we will want to change that to our working directory (e.g., ``cd``).
|
||||
|
||||
* Windows
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
(navigate) C:\Users\Username> cd Desktop
|
||||
(navigate) C:\Users\Username\Desktop> mkdir Code
|
||||
(navigate) C:\users\Username\Desktop> cd Code
|
||||
|
||||
* Linux/Mac
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
(navigate) MyComputer ~ $ mkdir ~/Desktop/Code
|
||||
(navigate) MyComputer ~ $ cd ~/Desktop/Code
|
||||
|
||||
.. _fork_repository:
|
||||
|
||||
**Working with a Fork**
|
||||
|
||||
For external contributors, we recommend forking the repository first. If you do not intend to contribute to the project, you can skip this step and clone the main repository directly.
|
||||
|
||||
1. Visit https://github.com/TheDeanLab/navigate and click the "Fork" button
|
||||
2. Clone your fork instead of the main repository:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
git clone https://github.com/YOUR-USERNAME/navigate.git
|
||||
cd navigate
|
||||
|
||||
3. Set up the upstream remote to keep your fork updated:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
git remote add upstream https://github.com/TheDeanLab/navigate.git
|
||||
|
||||
4. Create a branch for your changes:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
git checkout -b your-feature-branch
|
||||
|
||||
.. _clone_repository:
|
||||
|
||||
**Clone the GitHub repository**
|
||||
|
||||
For those who do not want to fork the repository, you can clone the main repository directly. This will create a local copy of the repository on your machine.
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
C:\Users\Username\Code> $ git clone https://github.com/TheDeanLab/navigate.git
|
||||
|
||||
.. _virtual_environment:
|
||||
|
||||
**Install navigate in a Virtual Environment**
|
||||
|
||||
We strongly recommend using a virtual environment for development. This can be accomplished either with Python's built-in `venv` or `conda`.
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
# Using venv
|
||||
python -m venv navigate-env
|
||||
|
||||
# On Windows
|
||||
navigate-env\Scripts\activate
|
||||
|
||||
# On Linux/Mac
|
||||
source navigate-env/bin/activate
|
||||
|
||||
# Then install Navigate
|
||||
pip install -e .[dev]
|
||||
|
||||
The same thing can be achieved using conda:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
# Using conda
|
||||
conda create -n navigate python=3.9.7
|
||||
conda activate navigate
|
||||
(navigate) C:\Users\Username\Code> cd navigate
|
||||
|
||||
# On Windows
|
||||
(navigate) C:\Users\Username\Code\navigate> pip install -e .[dev]
|
||||
|
||||
# On Linux/Mac
|
||||
(navigate) pip install -e '.[dev]'
|
||||
|
After Width: | Height: | Size: 103 KiB |
@@ -0,0 +1,56 @@
|
||||
=======================
|
||||
Software Architecture
|
||||
=======================
|
||||
|
||||
.. image:: images/architecture.png
|
||||
:align: center
|
||||
:alt: Software Architecture
|
||||
|
||||
.. _software-architecture-section:
|
||||
|
||||
Model View Controller (MVC)
|
||||
============================
|
||||
|
||||
The architecture of **navigate** is designed following the industry-standard Model-View-Controller (MVC) pattern. In this structure:
|
||||
|
||||
- **Model**: The model operates in its own subprocess and is responsible for communicating with hardware and performing image handling and processing tasks. Communication with the controller is managed through event queues, ensuring efficient data handling.
|
||||
- **View**: The view is responsible for displaying the user interface and communicating with the controller. Each graphical user interface (GUI) window, such as the camera display and autofocusing module, is controlled by a dedicated sub-controller. These sub-controllers are responsible for relaying information back to the main controller, maintaining a clear separation of functionality.
|
||||
- **Controller**: Acts as the central unit that manages the flow of data between the model and the view components, coordinating the application's overall functionality. It relays user input in the form of traces and commands to the model and relays model output in the form of images and data to the view.
|
||||
|
||||
Extendability
|
||||
============================
|
||||
|
||||
To maximize the extendability of **navigate**, it incorporates:
|
||||
|
||||
- **REST-API Level**: A RESTful API layer is included to facilitate communication with external libraries, ensuring compatibility and extendability. Data is exchanged to the external environment through a http server, allowing for rapid and seamless integration with other systems. Data does not need to be saved locally to be loaded by the external system.
|
||||
- **Plugin Layer**: Offers the flexibility to integrate non-supported devices through plugins, enhancing the system's adaptability to various hardware.
|
||||
|
||||
Data Acquisition and Processing
|
||||
===============================
|
||||
|
||||
**navigate** employs a feature container for running acquisition routines, characterized by:
|
||||
|
||||
- **Reconfigurable Workflows**: Supports custom data acquisition workflows, which are adaptable and can integrate computer vision feedback mechanisms for enhanced functionality.
|
||||
- **Threading and Parallelization**: To optimize performance, threading and parallelization techniques are extensively utilized, allowing for efficient handling of large objects and data processing.
|
||||
- **Tree Data Structure**: The system's backbone for alignment, imaging, and image analysis is a reconfigurable tree data structure. This enables the creation of customizable acquisition "recipes" tailored to specific specimens.
|
||||
- **Image Analysis Routines**: Custom routines for image analysis can also be loaded into **navigate** during run-time. Image analysis is performed on images in memory that are stored as numpy arrays, ensuring rapid processing.
|
||||
|
||||
Feature Lists
|
||||
============================
|
||||
|
||||
Feature lists are highly versatile, capable of:
|
||||
|
||||
- **Sequential Execution**: Acquisition routes can be executed in a predefined order, ensuring systematic data collection.
|
||||
- **Logic Gates Integration**: Incorporates conditional logic (e.g., if/then, try/except) and loop structures (while, for-loops), providing flexibility in data acquisition and processing.
|
||||
- **Non-Imaging Processes**: Supports the inclusion of non-imaging-based processes, such as solvent exchange, broadening the application scope of the system.
|
||||
|
||||
Microscope Objects
|
||||
============================
|
||||
|
||||
**navigate** supports the definition of multiple microscope objects, which can be configured in the `configuration.yaml` file. Each microscope object:
|
||||
|
||||
- **Independent or Shared Hardware**: Can have its own independent hardware or share hardware components with other microscope instances, providing flexibility in system design.
|
||||
- **Multi-Modal Imaging**: Enables seamless definition of multi-modal imaging systems, allowing for integration of different imaging modalities within the same workflow.
|
||||
- **Custom Acquisition Routines**: Supports the creation of distinct image acquisition routines for each microscope object, which can be switched dynamically as part of larger feature workflows or biological event handling.
|
||||
|
||||
This architecture allows for highly adaptable and reconfigurable imaging systems tailored to complex experimental needs.
|
||||
@@ -0,0 +1,193 @@
|
||||
.. _features:
|
||||
|
||||
=================
|
||||
Feature Container
|
||||
=================
|
||||
|
||||
**navigate** includes a **feature container** that enables reconfigurable acquisition and analysis workflows. The feature container runs a tree of **features**, where each feature may perform a *signal* operation, which modifies the state of the microscope's hardware, or a *data* operation, where it performs an analysis on acquired image data, or both.
|
||||
|
||||
Once a feature is executed, any features dependent on this feature's execution will execute (for example, move the stage, then snap a picture). Following this, the next set of features in sequence will be executed.
|
||||
|
||||
Examples of some existing features include ``navigate.model.features.common_features.ZStackAcquisition``, which acquires a z-stack, and ``navigate.model.features.autofocus.Autofocus``, which finds the ideal plane of focus of a sample using a discrete cosine transform.
|
||||
|
||||
-----------------
|
||||
|
||||
.. _feature_objects:
|
||||
|
||||
Feature Objects
|
||||
===============
|
||||
|
||||
Each feature is an object that accepts a pointer to ``navigate.model.model`` in its ``__init__()`` arguments and contains a configuration dictionary that dictates feature behavior in its ``__init__()`` function. A complete configuration dictionary is shown below. As few or as many of these options can be specified as needed. Each function is considered a leaf or node of the feature tree.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
self.config_table = {'signal': {'init': self.pre_func_signal,
|
||||
'main': self.in_func_signal,
|
||||
'end': self.end_func_signal,
|
||||
'cleanup': self.cleanup_func_signal},
|
||||
'data': {'init': self.pre_func_data,
|
||||
'main': self.in_func_data,
|
||||
'end': self.end_func_data,
|
||||
'cleanup': self.cleanup_func_data},
|
||||
'node': {'node_type': 'multi-step',
|
||||
'device_related': True,
|
||||
'need_response': True},
|
||||
}
|
||||
|
||||
Both ``signal`` and ``data`` configuration entries are themselves dictionaries that can contain ``init``, ``main``, ``end`` and/or ``cleanup`` entries.
|
||||
|
||||
- ``init`` entries dictate pre-processing steps that must be run before the main function of the feature starts.
|
||||
- ``main`` entries dictate the primary operation of the feature, and are run once per acquisition step. They return ``True`` if the acquisition should proceed and ``False`` if the acquisition should be ended.
|
||||
- ``end`` entries are run once per main function returning ``True``. They check if the acquisition should end, if we are at any boundary points of the ``main`` function (e.g. if we need to change positions in a multi-position z-stack acquisition), and describe any closing operations that must be performed when exiting the feature.
|
||||
- ``cleanup`` entries dictate what happens if the node fails. This is for failsafe controls such as "turn off all lasers."
|
||||
|
||||
The ``node`` configuration dictionary contains general properties of feature nodes. ``node_type`` can be ``one-step`` or ``multi-step``, the latter indicating we have an ``init``, a ``main`` and an ``end``. ``device_related`` is set to ``True`` if we have a ``multi-step`` signal container. ``need_response`` is set to true if the signal node waits on hardware (e.g. waits for a stage to confirm it has indeed moved) before proceeding.
|
||||
|
||||
Each of the functions that are the value entries in ``self.config_table`` dictionaries are methods of the feature object.
|
||||
|
||||
-----------------
|
||||
|
||||
Creating A Custom Feature Object
|
||||
--------------------------------
|
||||
|
||||
Each feature object is defined as a class. Creating a new feature is the same as creating any Python class, but with a few requirements. The first parameter of the ``__init__`` function (after ``self``) must be ``model``, which gives the feature object full access to the **navigate** model. All the other parameters are keyword arguments and must have default values. The ``__init__`` function should always have a ``config_table`` attribute (see :ref:`above <feature_objects>` for a description of the ``config_table``).
|
||||
|
||||
In the example below, we will create a custom feature that moves to a specified position in **navigate**'s multi-position table and calculates the sharpness of the image at this position using the Normalized DCT Shannon Entropy metric. An example ``__init__()`` function for our ``FeatureExample`` class is below.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from navigate.model.analysis.image_contrast import fast_normalized_dct_shannon_entropy
|
||||
|
||||
class FeatureExample:
|
||||
|
||||
def __init__(self, model, position_id=0):
|
||||
self.model = model
|
||||
self.position_id = position_id
|
||||
|
||||
self.config_table = {
|
||||
"signal": {
|
||||
"init": self.pre_func_signal,
|
||||
"main": self.in_func_signal,
|
||||
},
|
||||
"data": {
|
||||
"main": self.in_func_data,
|
||||
},
|
||||
"node": {
|
||||
"device_related": True,
|
||||
}
|
||||
}
|
||||
|
||||
- Get multi-position table position from the GUI.
|
||||
|
||||
All the GUI parameters are stored in ``model.configuration["experiment"]`` during runtime. Below, we create a function to get all the position stored at ``position_id`` from the multi-position table in the GUI when we launch our feature.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
def pre_func_signal(self):
|
||||
positions = self.model.configuration["experiment"]["MultiPositions"]
|
||||
if self.position_id < len(positions):
|
||||
self.target_position = positions[self.position_id]
|
||||
else:
|
||||
current_position = self.model.get_stage_position()
|
||||
self.target_position = dict([(axis[:-4], value) for axis, value in current_position.items()])
|
||||
|
||||
More GUI parameters can be found in `experiment.yml <https://github.com/TheDeanLab/navigate/blob/develop/src/navigate/config/experiment.yml>`_
|
||||
|
||||
- Use the stage to move to this position.
|
||||
|
||||
Now, we move stage to the ``target_position`` we grabbed from the multi-position table.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
def in_func_signal(self):
|
||||
pos = dict([(f"{axis}_abs", value) for axis, value in self.target_position.items()])
|
||||
self.model.move_stage(pos, wait_until_done=True)
|
||||
|
||||
- Take a picture and process the resulting image.
|
||||
|
||||
In parallel with our signal function call, the camera will acquire an image. The image captured by the camera will be stored in the ``model`` ``data_buffer``. The ``data`` functions run after an image is acquired. We add code to deal with this image in the ``"main"`` data function. Here, we calculate the Shannon entropy of the image.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
def in_func_data(self, frame_ids):
|
||||
for id in frame_ids:
|
||||
image = self.model.data_buffer[id]
|
||||
entropy = fast_normalized_dct_shannon_entropy(image, psf_support_diameter_xy=3)
|
||||
print("entropy of image:", id, entropy)
|
||||
|
||||
Now, we've created a whole new feature and can use it as we wish.
|
||||
|
||||
How to interact with other devices
|
||||
----------------------------------
|
||||
|
||||
We interact with all devices through ``self.model.active_microscope``. Here is an example to open shutter:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
self.model.active_microscope.shutter.open_shutter()
|
||||
|
||||
How to pause and resume data threads in the model
|
||||
-------------------------------------------------
|
||||
|
||||
The image data acquired from the camera are handled in an independent thread. As such, the *signal* and *data* operations by default run in parallel and do not block each other. Sometimes, we want to be sure a device is ready or has moved. For example, in ``FeatureExample``, we have no guarantee that the stage finished moving before the image was taken. The ``wait_until_done`` call only blocks the signal thread from progressing before the stage finishes its move. To ensure the data thread also waits, we need to pause the data thread until the stage is ready.
|
||||
|
||||
Here is an example of how we can pause and resume the data thread:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
self.model.pause_data_thread()
|
||||
self.model.move_stage(pos, wait_until_done=True)
|
||||
# ...
|
||||
self.model.resume_data_thread()
|
||||
|
||||
We can of course replace ``self.model.move_stage(pos, wait_until_done=True)`` with whatever task we want to wait for before resuming image acquisition.
|
||||
|
||||
Model functions can be found :doc:`in the API <../../05_reference/_autosummary/navigate.model.model.Model>`.
|
||||
|
||||
Custom Feature Lists
|
||||
====================
|
||||
|
||||
The **navigate** software allows you to chain feature objects into lists to build acquisition workflows.
|
||||
|
||||
Creating a Custom Feature List in Python
|
||||
----------------------------------------
|
||||
|
||||
To create a customized feature list, follow these steps:
|
||||
|
||||
- Import the necessary modules:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from navigate.tools.decorators import FeatureList
|
||||
from navigate.model.features.feature_related_functions import *
|
||||
|
||||
``FeatureList`` is a decorator that registers the list of features. ``feature_related_functions`` contains convenience imports that allow us to call ``PrepareNextChannel`` instead of ``navigate.model.features.common_features.PrepareNextChannel``. These functions make for more readable code.
|
||||
|
||||
- Create the feature list.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
@FeatureList
|
||||
def feature_example():
|
||||
return [
|
||||
(
|
||||
{"name": PrepareNextChannel},
|
||||
{
|
||||
"name": LoopByCount,
|
||||
"args": ("channels",),
|
||||
},
|
||||
)
|
||||
]
|
||||
|
||||
In this example, the feature list takes one image per selected channel in the GUI. ``PrepareNextChannel`` sets up the channel and ``LoopByCount`` calls this setup once per selected channel.
|
||||
|
||||
- Now, open **navigate**.
|
||||
- Go to the :guilabel:`Features` menu.
|
||||
|
||||
.. image:: images/step_1.png
|
||||
|
||||
- Import the customized feature. Select :guilabel:`Add Custom Feature List` from the :guilabel:`Features` menu. A dialog box will appear, allowing you to select the Python file containing your customized feature list function.
|
||||
|
||||
.. image:: images/step_2.png
|
||||
|
||||
- Choose the Python file containing your customized feature list function. **navigate** will load the specified feature list, making it available for use in your experiments and analyses. It will appear at the bottom of the :guilabel:`Features` menu.
|
||||
|
After Width: | Height: | Size: 232 KiB |
|
After Width: | Height: | Size: 244 KiB |
8
docs/source/03_contributing/05_restapi/restapi.rst
Normal file
@@ -0,0 +1,8 @@
|
||||
========
|
||||
REST-API
|
||||
========
|
||||
|
||||
**navigate** has the ability to communicate with other image analysis software through REST-API interfaces. In general, the REST-API is used to communicate with software that has different or conflicting dependencies with the **navigate** codebase. Data is transferred via HTTP requests and responses,
|
||||
which is faster and more efficient than locally saving the data and then loading it into another piece of software, but slower than direct access of the data in memory.
|
||||
|
||||
An example of using our REST-API to communicate with ilastik, a widely used image segmentation tool, is provided in our Case Studies section :ref:`here <case_study_ilastik>`. The navigate ilastik Server plugin can be found `here <https://github.com/TheDeanLab/navigate-ilastik-server>`_.
|
||||
BIN
docs/source/03_contributing/06_plugin/images/plugin_1.png
Normal file
|
After Width: | Height: | Size: 110 KiB |
BIN
docs/source/03_contributing/06_plugin/images/plugin_2.png
Normal file
|
After Width: | Height: | Size: 119 KiB |
BIN
docs/source/03_contributing/06_plugin/images/plugin_3.png
Normal file
|
After Width: | Height: | Size: 12 KiB |
BIN
docs/source/03_contributing/06_plugin/images/plugin_4.png
Normal file
|
After Width: | Height: | Size: 111 KiB |
BIN
docs/source/03_contributing/06_plugin/images/plugin_5.png
Normal file
|
After Width: | Height: | Size: 21 KiB |
BIN
docs/source/03_contributing/06_plugin/images/plugin_6.png
Normal file
|
After Width: | Height: | Size: 20 KiB |
227
docs/source/03_contributing/06_plugin/plugin_home.rst
Normal file
@@ -0,0 +1,227 @@
|
||||
.. _plugin:
|
||||
|
||||
====================
|
||||
Plugin Architecture
|
||||
====================
|
||||
|
||||
**navigate** is designed with extensibility. Users can seamlessly integrate custom GUI plugins, device plugins, new feature plugins, and even define specific acquisition modes.
|
||||
|
||||
-------------------------------------
|
||||
|
||||
Introduction to Plugins
|
||||
########################
|
||||
|
||||
The **navigate** **plugin system** gives users the flexibility to extend its functionality according to their specific needs. **navigate** will load plugins automatically and users can use their plugins with **navigate** seamlessly.
|
||||
|
||||
-----------
|
||||
|
||||
Installing a Plugin
|
||||
####################
|
||||
|
||||
Once you've built a plugin or downloaded a **navigate** plugin, you can easily install it. Here, we have downloaded the `Navigate Confocal-Projection Plugin <https://github.com/TheDeanLab/navigate-confocal-projection>`_.
|
||||
|
||||
#. You can install the **navigate-confocal-projection** plugin by selecting the menu :menuselection:`Plugins --> Install Plugin`.
|
||||
|
||||
.. image:: images/plugin_1.png
|
||||
:width: 60%
|
||||
:align: center
|
||||
|
||||
#. Select the folder `ConfocalProjectionPlugin` and click :guilabel:`Select`.
|
||||
|
||||
.. image:: images/plugin_2.png
|
||||
:width: 60%
|
||||
:align: center
|
||||
|
||||
#. You should receive a message indicating that the plugin has been installed successfully.
|
||||
|
||||
.. image:: images/plugin_3.png
|
||||
:width: 60%
|
||||
:align: center
|
||||
|
||||
#. Restart **navigate** to use this installed plugin.
|
||||
|
||||
-----------
|
||||
|
||||
Uninstalling a Plugin
|
||||
#####################
|
||||
|
||||
Uninstalling a plugin is very easy.
|
||||
|
||||
#. Select :menuselection:`Plugins --> Uninstall Plugins`. This will open a popup window where you can see all of the currently installed plugins.
|
||||
|
||||
.. image:: images/plugin_4.png
|
||||
:width: 60%
|
||||
:align: center
|
||||
|
||||
#. Select the plugin you want to uninstall.
|
||||
|
||||
.. image:: images/plugin_5.png
|
||||
:width: 60%
|
||||
:align: center
|
||||
|
||||
#. Click :guilabel:`Uninstall`.
|
||||
|
||||
.. image:: images/plugin_6.png
|
||||
:width: 60%
|
||||
:align: center
|
||||
|
||||
#. Restart **navigate** to fully remove the uninstalled plugin.
|
||||
|
||||
-------------------------------------
|
||||
|
||||
Designing a Plugin
|
||||
##########################
|
||||
|
||||
Using a Plugin Template
|
||||
-------------------------------------
|
||||
|
||||
A comprehensive **plugin template** is provided. Users could download the **plugin template** from `github <https://github.com/TheDeanLab/navigate-plugin-template>`_ and build plugins on it.
|
||||
|
||||
Plugin Structure:
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
plugin_name/
|
||||
├── controller/
|
||||
│ ├── plugin_name_controller.py
|
||||
│ ├── ...
|
||||
├── model/
|
||||
│ ├── devices/
|
||||
│ │ └── plugin_device/
|
||||
│ │ ├── device_startup_functions.py
|
||||
│ │ ├── plugin_device.py
|
||||
│ │ └── synthetic_plugin_device.py
|
||||
│ └── features/
|
||||
│ ├── plugin_feature.py
|
||||
│ ├── ...
|
||||
│
|
||||
├── view/
|
||||
│ ├── plugin_name_frame.py
|
||||
│ ├── ...
|
||||
│
|
||||
├── feature_list.py
|
||||
├── plugin_acquisition_mode.py
|
||||
└── plugin_config.yml
|
||||
|
||||
.. note::
|
||||
|
||||
The template shows a plugin with GUI, device, feature, feature_list and acquisition mode. If your plugin only incorporates some of these components, you should remove unused folders and files.
|
||||
|
||||
-------------------------------------
|
||||
|
||||
Plugin Configuration
|
||||
--------------------
|
||||
|
||||
There should always have a ``plugin_config.yml`` file under the plugin folder, which tells **navigate** the plugin name, the GUI as a Tab or Popup and custom acquisition mode name. A typical plugin config is:
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
name: Plugin Name
|
||||
view: Popup # or Tab
|
||||
acquisition_modes:
|
||||
- name: Plugin Acquisition
|
||||
file_name: plugin_acquisition_mode.py
|
||||
|
||||
-------------------------------------
|
||||
|
||||
Plugin GUI Elements
|
||||
--------------------
|
||||
|
||||
**navigate** supports plugins with their own GUIs. A custom plugin GUI can be integrated as a tab or a popup. Users should specify a view option in ``plugin_config.yml``. If it is a popup, users can find the plugin under the :guilabel:`Plugins` menu in the **navigate** window. If it is a tab, it will appear next to the :ref:`Settings Notebooks <ui_settings_notebooks>`.
|
||||
|
||||
When creating a new plugin with a GUI, ensure that the plugin name is consistent with the naming conventions for the associated Python files (``plugin_name_controller.py`` and ``plugin_name_frame.py``). Both Python filenames should be in lowercase.
|
||||
|
||||
For example, if your plugin is named "My Plugin" (there is a space in between), the associated Python files should be named: ``my_plugin_frame.py`` and ``my_plugin_controller.py``.
|
||||
|
||||
-------------------------------------
|
||||
|
||||
Plugin Devices
|
||||
------------------
|
||||
|
||||
The **navigate** plugin architecture allows you to integrate new hardware device. There can be more than one device inside a plugin. If they are different kinds of device, please put them into different folders. For each kind of device, there should be a ``device_startup_functions.py`` telling **navigate** how to start the device and indicating the reference name of the device to be used in ``configuration.yaml``.
|
||||
|
||||
Device type name and reference name are given as following:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
DEVICE_TYPE_NAME = "plugin_device" # Same as in configuration.yaml, for example "stage", "filter_wheel", "remote_focus_device"...
|
||||
DEVICE_REF_LIST = ["type", "serial_number"]
|
||||
|
||||
A function to load the device connection should be given,
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
def load_device(hardware_configuration, is_synthetic=False):
|
||||
# ...
|
||||
return device_connection
|
||||
|
||||
A function to start the device should be given,
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
def start_device(microscope_name, device_connection, configuration, is_synthetic=False):
|
||||
# ...
|
||||
return device_object
|
||||
|
||||
The template for ``device_startup_functions.py`` can be found in the `plugin template <https://github.com/TheDeanLab/navigate-plugin-template/blob/main/plugins_template/model/devices/plugin_device/device_startup_functions.py>`_.
|
||||
|
||||
-------------------------------------
|
||||
|
||||
Plugin Features
|
||||
-------------------------
|
||||
|
||||
**navigate** allows users to add new features. New feature objects and feature lists can each be a plugin or components of a plugin. Features and feature lists are automatically loaded into **navigate**.
|
||||
|
||||
Please visit `here <https://thedeanlab.github.io/navigate/contributing/feature_container.html>`_ for details about how to build a new feature object and feature list.
|
||||
|
||||
-------------------------------------
|
||||
|
||||
Custom Acquisition Modes
|
||||
------------------------
|
||||
|
||||
Navigate offers seamless support for custom acquisition modes, and registering a new mode is straightforward.
|
||||
|
||||
1. Download the template for `plugin_acquisition_mode.py <https://github.com/TheDeanLab/navigate-plugin-template/blob/main/plugins_template/plugin_acquisition_mode.py>`_
|
||||
|
||||
2. Update the ``feature_list``.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
@AcquisitionMode
|
||||
class PluginAcquisitionMode:
|
||||
def __init__(self, name):
|
||||
self.acquisition_mode = name
|
||||
|
||||
self.feature_list = [
|
||||
# update here
|
||||
]
|
||||
|
||||
3. Update the functions.
|
||||
|
||||
Users should tell **navigate** what to do before and after acquisition using the following functions.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
def prepare_acquisition_controller(self, controller):
|
||||
# update here
|
||||
|
||||
def end_acquisition_controller(self, controller):
|
||||
# update here
|
||||
|
||||
def prepare_acquisition_model(self, model):
|
||||
# update here
|
||||
|
||||
def end_acquisition_model(self, model):
|
||||
# update here
|
||||
|
||||
4. Register the acquisition mode in ``plugin_config.yml``.
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
acquisition_modes:
|
||||
- name: Custom Acquisition
|
||||
file_name: plugin_acquisition_mode.py
|
||||
|
||||
-----------
|
||||
|
||||
For more plugin examples, please visit the plugins in the table of contents menu on the left.
|
||||