NeuroAnalyzer.test_plugin()NeuroAnalyzer Plugins
NeuroAnalyzer can be extended with plugins: Julia source files that add new functions (analyses, plots, importers, …) without modifying the package itself.
How plugins work
Plugins are stored in the $HOME/NeuroAnalyzer/plugins folder (the folder is created when NeuroAnalyzer is loaded for the first time; its location is stored in NeuroAnalyzer.plugins_path). Each plugin has its own folder with a src subfolder:
$HOME/NeuroAnalyzer/
|- pipelines
|- plugins
|- test_plugin
|- src
|- test_plugin.jl
|- another_plugin
|- src
|- another_plugin.jl
|- helpers.jl
Every .jl file in the src subfolder is included into the NeuroAnalyzer module:
- when NeuroAnalyzer is loaded (
using NeuroAnalyzer) - after a plugin is installed, updated or removed
- when
plugins_reload()is called
Plugin functions are part of the NeuroAnalyzer module and are called with the module prefix:
Tip: Plugins are ordinary Julia code executed with your user permissions. Install plugins only from sources you trust.
Managing plugins
Listing installed plugins:
plugins_list()Reloading all installed plugins (e.g. after editing a plugin file):
plugins_reload()Installing
Plugins are installed from Git repositories (the URL must start with https://, http:// or git@); the repository is cloned into the plugins folder:
plugins_install("https://codeberg.org/AdamWysokinski/plot_ITPC.jl")Plugins may also be installed from local .zip or .tar.gz archives. The archive is extracted into the plugins folder, so it must contain the plugin folder (with its src subfolder) at the top level:
plugins_install("/home/user/Downloads/plot_ITPC.jl-main.zip")Tip: Installing from a local archive requires the unzip or tar command.
Tip: Existing files are overwritten when installing from a local archive.
Updating
Plugins installed from Git repositories are updated with git pull; plugins installed from archives are skipped (install the new archive instead).
Updating all plugins:
plugins_update()Updating a single plugin (the name of its folder):
plugins_update("plot_ITPC.jl")Removing
Removing a plugin deletes its whole folder:
plugins_remove("plot_ITPC.jl")Tip: Functions of a removed plugin stay available until Julia is restarted.
Writing a plugin
A minimal plugin is a folder with a single file:
plugins/
|- my_plugin
|- src
|- my_plugin.jl
Because plugin files are included into the NeuroAnalyzer module, a plugin can use all NeuroAnalyzer functions, including the internal ones (e.g. _info(), _warn()), and all packages NeuroAnalyzer depends on. Packages that are not NeuroAnalyzer dependencies cannot be loaded from a plugin.
Recommended conventions:
- use a common prefix for all names (e.g.
my_plugin_…), so they do not clash with NeuroAnalyzer functions or with other plugins (a clash silently overwrites the existing function) - follow the NeuroAnalyzer conventions: a private method
_name(s)working on arrays and a public methodname(obj; ch)working on NEURO objects;obj.datais anArray{Float64, 3}(channels × samples × epochs) - validate the arguments and throw
ArgumentErrorwith a clear message - add a docstring, so that
?NeuroAnalyzer.nameshows help - add a self-test function, so that users can check whether the plugin works
The bundled test_plugin is a complete example: it calculates the zero-crossing rate of the selected channels and contains a self-test.
"""
_test_plugin_zcr(s; fs)
Zero-crossing rate of a signal (crossings per second).
"""
function _test_plugin_zcr(s::AbstractVector; fs::Int64)::Float64
fs >= 1 || throw(ArgumentError("fs must be ≥ 1."))
length(s) >= 2 || throw(ArgumentError("Signal must contain at least 2 samples."))
return _zeros(s) / (length(s) / fs)
end
"""
test_plugin_zcr(obj; ch)
Zero-crossing rate of the selected channels, shape (channels, epochs).
"""
function test_plugin_zcr(obj::NeuroAnalyzer.NEURO; ch::Union{String, Vector{String}, Regex})::Matrix{Float64}
ch = get_channel(obj; ch = ch)
isempty(ch) && throw(ArgumentError("No channels selected."))
zcr = zeros(length(ch), nepochs(obj))
for (idx, c) in enumerate(ch), ep in 1:nepochs(obj)
zcr[idx, ep] = _test_plugin_zcr(obj.data[c, :, ep]; fs = sr(obj))
end
return zcr
endUsing the plugin:
eeg = import_edf("files/eeg.edf")
zcr = NeuroAnalyzer.test_plugin_zcr(eeg; ch = "eeg")
# self-test: zero-crossing rate of a 10 Hz sine must be 20 per second
NeuroAnalyzer.test_plugin()During development, edit the plugin file and call plugins_reload() to load the new version without restarting Julia.