NetTracer3D Plugin System
NetTracer3D supports a plugin system that allows both developers and users to extend the application with new analysis tools, processing methods, visualisations, and integrations — without modifying the core codebase.
Plugins are discovered automatically at startup and managed through the Extensions panel in the menu bar.
User Guide
What Are Plugins?
Plugins are small Python modules that add functionality to NetTracer3D. They can add new menu items, new right-click options, new analysis tabs, custom overlays, or entirely new dialog windows. Several plugins ship with NetTracer3D by default (e.g. Cellpose Segmentation, Cell Preview Grid, Channel Expansion), and you can install additional ones or write your own.
Where Do Plugins Live?
NetTracer3D searches for plugins in several locations, in order:
User plugin directory
~/.nettracer3d/plugins/
On Windows this is typically
C:\Users\<you>\.nettracer3d\plugins\. Place any.pyfile or plugin folder here and NetTracer3D will find it on the next launch. The Extensions panel has an Open User Plugin Folder button that opens this directory in your file manager.Package plugin directory
<python>/Lib/site-packages/nettracer3d/plugins/
Plugins that ship with the
pip install nettracer3dpackage live here. You generally do not need to touch this directory — it is populated automatically by the installer.Environment variable
Set
NETTRACER3D_PLUGIN_PATHto a colon-separated (or semicolon-separated on Windows) list of directories containing additional plugins.Pip entry points
Plugins distributed as their own pip packages can declare the entry point group
nettracer3d.pluginsand be discovered automatically afterpip install.
The Extensions Panel
Open the Extensions panel from the menu bar:
Extensions → Manage Extensions…
The panel shows every discovered plugin, colour-coded by status:
Colour |
Meaning |
|---|---|
Green |
Loaded — the plugin is active and its menu items / hooks are registered. |
Blue |
Needs Deps — the plugin was found but could not be imported because one or more Python packages are missing. Click Install Deps to install them automatically via pip. |
Red |
Failed — the plugin raised an error during import or registration. Select it to see the error traceback. |
Orange |
Incompatible — the plugin requires a newer version of the plugin API than this build of NetTracer3D provides. |
Grey |
Disabled — you manually disabled this plugin. Click Enable to re-activate it. |
Available buttons:
Enable — re-enable a disabled plugin and attempt to load it.
Disable — unload the plugin and prevent it from loading on future launches.
Install Deps — (pip environments only) reads the plugin’s
requirements.txt, asks about GPU / CUDA preferences if PyTorch is involved, and runspip installin the current environment.Reload — unload and re-import the plugin without restarting NetTracer3D. Useful during development.
Rescan — re-scan all plugin directories for new files and attempt to load any newly discovered plugins.
Installing Plugin Dependencies
When a plugin is marked Needs Deps (blue):
Select it in the list.
Click Install Deps.
If the plugin requires PyTorch, a dialog appears asking which GPU / CUDA version you have. The manager will try to auto-detect your CUDA installation. Choose Auto-detect unless you know you need a specific version.
A confirmation dialog shows exactly which packages will be installed and the full pip command. Click Yes to proceed.
pip runs in the background. When it finishes, the plugin is automatically loaded.
Note
If you are running the compiled (PyInstaller / installer) version of
NetTracer3D, pip is not available. Plugins for the compiled version
must be distributed with a _vendor/ folder containing
pre-compiled dependencies. See the Developer Guide below for details.
Built-In Plugins
Cellpose Segmentation
Integrates the Cellpose instance segmentation pipeline directly into NetTracer3D.
Menu: Extensions → Cellpose → Open Cellpose Panel…
Choose which channel to segment and optionally a secondary context channel (e.g. a nuclear stain).
Select a model (built-in or custom
.pthfile).Adjust parameters: diameter, flow threshold, cell probability threshold, minimum size, stitch threshold.
Enable Chunked Processing to segment large images in pieces that fit in GPU memory.
Dimensionality (2-D vs 3-D) is auto-detected from the input data.
The segmented mask is written to the channel of your choice.
Requires:
cellpose>=3.0(installed via the Extensions panel).
Developer Guide
This section explains how to write, package, and distribute your own NetTracer3D plugins.
Plugin Structure
Please reference the built in Cellpose plugin _init_.py and requirements.txt files for a clear example of how to integrate a plugin
A plugin is either a single .py file or a folder (Python package).
Single file (no dependencies beyond NetTracer3D base):
my_plugin.py
Folder / package (has its own dependencies or bundled assets):
my_plugin/
├── __init__.py # plugin code
├── requirements.txt # pip dependencies
└── _vendor/ # (optional) bundled deps for PyInstaller
Every plugin must expose two things at module level:
PLUGIN_INFO— a dictionary of metadata.register(api)— a function called once when the plugin loads.
Optionally:
unregister(api)— called when the plugin is disabled or the app closes.
PLUGIN_INFO
PLUGIN_INFO = {
'name': 'My Plugin', # display name
'version': '1.0.0', # semver string
'author': 'Your Name',
'description': 'What it does.',
'api_version': (1, 0), # minimum API version required
'requires': [], # other plugin names (inter-plugin deps)
'category': 'analysis', # analysis | processing | visualization | io | other
}
register() and unregister()
_api = None
def register(api):
"""Called once when the plugin is loaded."""
global _api
_api = api
# Register menu items, event listeners, display hooks, etc.
api.register_menu_action(
"Extensions/My Plugin/Do Something",
my_callback)
api.on("slice_changed", on_slice_changed)
def unregister(api):
"""Called when the plugin is disabled or the app closes."""
# Clean up any resources.
pass
Plugin API Reference
The api object passed to register() is an instance of
PluginAPI. All methods listed below are part of the stable public
API and will not change without a major version bump.
Data Access — Read
Method |
Description |
|---|---|
|
Return a reference to channel data (0 = Nodes, 1 = Edges, 2 = Overlay 1, 3 = Overlay 2). |
|
Return the four channel names. |
|
Index of the currently selected channel. |
|
Current Z-slice index. |
|
|
|
Deep copy of |
|
The current highlight overlay array. |
|
The networkx Graph object. |
|
|
|
|
|
|
|
|
|
|
|
Physical pixel size in XY. |
|
Physical pixel size in Z. |
|
Indices of currently visible channels. |
Data Access — Write
Write methods validate inputs, update the UI, and trigger display refreshes automatically.
Method |
Description |
|---|---|
|
Replace channel data. Handles shape validation, undo snapshot, button/slider state, and display refresh. |
|
Update the highlight overlay from index lists. |
|
Replace the community partition. |
|
Replace node identities. |
|
Replace node centroids. |
|
Update the XY physical scale. |
|
Update the Z physical scale. |
UI Output
Method |
Description |
|---|---|
|
Add a pandas DataFrame as a tab in the upper-right data panel. |
|
Format a Python dict into a table tab. |
|
Add an arbitrary QWidget as a tab. |
|
Show a message box. |
|
Print a message to the console. |
Display Hooks
Method |
Description |
|---|---|
|
Register a function called at the end of every display update.
Signature: |
|
Add a pyqtgraph graphics item to the image view. |
|
Remove a previously added graphics item. |
Events
Subscribe to application events with api.on(event, callback).
The callback receives a single data argument whose type depends on
the event.
Event |
Data |
Fired When |
|---|---|---|
|
|
User navigates to a different Z slice. |
|
|
User clicks or rectangle-selects nodes/edges. |
|
|
A channel’s data is loaded or replaced. |
|
|
A channel is deleted. |
|
|
The network graph is recalculated. |
|
|
The community partition is updated. |
|
|
Node identities are updated. |
|
|
Node centroids are updated. |
|
|
A previous session is loaded. |
|
|
The current session is saved. |
|
|
Another plugin finishes loading. |
|
|
A plugin is unloaded. |
|
|
The display finishes a full redraw. |
Utilities
Method |
Description |
|---|---|
|
Request a full display redraw. |
|
Change the current Z slice. |
|
The current API version as a |
Unsafe Escape Hatches
Warning
These methods return direct references to internal objects. Anything accessed through them may be renamed, removed, or restructured in any future release without notice. Use only for prototyping or accessing functionality not yet in the public API. Do not ship published plugins that depend on internals obtained this way.
Method |
Description |
|---|---|
|
Returns the |
|
Returns the |
Dependency Management
Plugins declare their Python dependencies in a requirements.txt file
placed alongside the plugin code.
Pip environments (pip install nettracer3d)
When the plugin manager cannot import a plugin due to a missing package,
it checks for requirements.txt in the plugin’s directory. If found,
the plugin is marked Needs Deps instead of Failed. The user can
then click Install Deps in the Extensions panel.
The requirements.txt uses standard pip format:
cellpose>=3.0
some-other-package
You do not need to list transitive dependencies — pip resolves them
automatically. For example, listing cellpose>=3.0 is sufficient;
torch and all of cellpose’s other dependencies are pulled in
automatically.
If your plugin requires PyTorch, the plugin manager will detect this
from the requirements file and present a GPU / CUDA selection dialog
before running pip. It auto-detects the installed CUDA version via
nvidia-smi, nvcc, or an existing torch installation, and adds
the appropriate --extra-index-url to the pip command.
PyInstaller / compiled builds
In a frozen (PyInstaller) environment, pip is not available. Plugins
must bundle their dependencies in a _vendor/ folder:
my_plugin/
├── __init__.py
├── requirements.txt # still included for reference
└── _vendor/
├── cellpose/
├── torch/
└── ...
The plugin manager detects sys.frozen, prepends _vendor/ to
sys.path before importing the plugin, and the bundled packages
resolve normally.
To create a _vendor/ folder:
pip install --target my_plugin/_vendor cellpose
# For GPU support:
pip install --target my_plugin/_vendor cellpose torch \
--extra-index-url https://download.pytorch.org/whl/cu124
Note
_vendor/ folders can be very large (>1 GB with PyTorch + CUDA).
Consider distributing CPU-only and GPU versions separately.
Packaging for PyPI
If you want your plugin to be installable via pip and auto-discovered:
Create a Python package with an entry point:
# pyproject.toml [project.entry-points."nettracer3d.plugins"] my_plugin = "my_package.my_plugin"
Your module must expose
PLUGIN_INFOandregister(api)at the top level of the entry point target.After
pip install my-plugin-package, NetTracer3D will discover it automatically on the next launch.
Alternatively, for plugins bundled inside the nettracer3d package
itself (i.e. shipped with the default install), place the plugin folder
in nettracer3d/plugins/ and add a package-data directive to
pyproject.toml:
[tool.setuptools.package-data]
"nettracer3d.plugins" = [
"*/requirements.txt",
"*/_vendor/**/*",
]
This ensures that requirements.txt files and _vendor/ contents
are included in the wheel alongside the Python code.
Minimal Example Plugin
"""
Example plugin that adds a menu item to count objects in the
active channel.
"""
import numpy as np
PLUGIN_INFO = {
"name": "Object Counter",
"version": "0.1.0",
"author": "Your Name",
"description": "Count unique non-zero labels in the active channel.",
"api_version": (1, 0),
"requires": [],
"category": "analysis",
}
_api = None
def register(api):
global _api
_api = api
api.register_menu_action(
"Extensions/Object Counter/Count Objects",
_count_objects,
)
def _count_objects():
channel = _api.get_active_channel()
data = _api.get_channel_data(channel)
if data is None:
_api.show_message("No Data", "Active channel is empty.", "warning")
return
unique = np.unique(data)
n = len(unique) - (1 if 0 in unique else 0)
_api.show_message(
"Object Count",
f"Channel {channel}: {n} unique objects",
"info",
)
Complete Plugin Checklist
✓ PLUGIN_INFO dict with name, version, api_version, category
✓ register(api) function
✓ unregister(api) function (optional but recommended)
✓ requirements.txt if any non-base dependencies
✓ _vendor/ folder if distributing for PyInstaller
✓ Menu items under "Extensions/<Your Plugin>/" namespace
✓ Console output prefixed with [YourPlugin] for debuggability
✓ Error handling — plugins should not crash the host application
✓ No direct access to internals (use api methods; get_unsafe_*
only as a last resort with the understanding it may break)