---
title: Creating a new plugin
sidebar_label: Creating a Plugin
---

# Creating a New Plugin

This guide explains how to create a new MNE Analyze plugin, including implementing the required virtual functions, setting up GUI elements, and integrating with the event system.

## Overview

All MNE Analyze plugins inherit from `AbstractPlugin` (`applications/mne_analyze/libs/anShared/Plugins/abstractplugin.h`). Unlike MNE Scan, MNE Analyze does not differentiate between plugin types.

Plugins are loaded at runtime from `bin/mne_analyze_plugins`. Once loaded, MNE Analyze retrieves each plugin's menus, controls, and views and displays them to the user. Currently each plugin returns at most one of each GUI element.

For implementation details, see:
- `analyzecore.cpp` → `initPluginManager()` — how plugins are discovered and loaded
- `mainwindow.cpp` constructor — how GUI elements are assembled

## Sample Plugin

A sample plugin with no functionality is included in `applications/mne_analyze/plugins/sampleplugin`. To create a new plugin, duplicate that folder and replace all instances of `SamplePlugin` with your plugin name. See the checklist at the end of this guide for details that are easy to miss.

## Deriving from AbstractPlugin

AbstractPlugin has a number of pure virtual functions that need to be defined by any new plugin. Among them are functions for getting the view, control, and menu GUI items for the plugin, as well subscribing to and receiving events from the event manager.

### clone()

Returns an instance of the plugin. This is not a copy. Most of the existing plugins do something like this:

```cpp
return QSharedPointer<AbstractPlugin>(new Averaging);
```

### init()

Initializes the plugin. This gets called after all the plugins get loaded into the Plugin Manager. Any startup actions that need to be done before the user begins interacting with the plugin, such as allocating memory or initializing parameters, can be done here.

### unload()

Closes the plugin. Gets called when the plugins are being shutdown when the main window is closed. Any shutdown actions that need to be performed before the program shuts down can be done here.

### getName()

Returns the plugin name, this will be used to set the plugin title on any views, controls, and menu items the plugin has.

### getMenu()

Returns menu items to be added to the top dropdown menu bar. If the plugin does not need a menu, return a `Q_NULLPTR` (null pointer) instead.

Create a menu object to return, and populate it with relevant actions. Below is a section of the data loader plugin as an example:

```cpp
QMenu* pMenuFile = new QMenu(tr("File"));

QAction* pActionLoad = new QAction(tr("Open"));
pActionLoad->setStatusTip(tr("Load a data file"));
connect(pActionLoad, &QAction::triggered,
        this, &DataLoader::onLoadFilePressed);
```

If the name of the menu matches an existing one, the new actions will be added to that existing menu, otherwise a new one will be created.

### getControl()

Returns a `QDockWidget*` containing the control GUI elements of the plugin. If the plugin does not need controls, return a `Q_NULLPTR` (null pointer) instead. Here we also set the allowed docking areas and the tab name that appears when the plugin is docked.

The controls are set by adding any `QWidget` to the `QDockWidget` that needs to be returned. Below is an example with a single widget, the tab name set to the plugin name, docked on the left or right sides of the window:

```cpp
QDockWidget* pControlDock = new QDockWidget(getName());
pControlDock->setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea);
pControlDock->setObjectName(getName());

QPushButton* pSingleButton = new QPushButton("Compute");
pControlDock->setWidget(pSingleButton);
```

This is useful if the control widget is already created. If instead the control widget needs to be created or multiple widgets need to be added, a layout can be used:

```cpp
QDockWidget* pControlDock = new QDockWidget(getName());
pControlDock->setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea);
pControlDock->setObjectName(getName());

QScrollArea* wrappedScrollArea = new QScrollArea(pControlDock);
QVBoxLayout* pLayout = new QVBoxLayout;
pControlDock->setWidget(wrappedScrollArea);

QPushButton* pFirstButton = new QPushButton("Compute");
QPushButton* pSecondButton = new QPushButton("Clear");

pLayout->addWidget(pFirstButton);
pLayout->addWidget(pSecondButton);
```

Many control widgets are already implemented in the Disp library and can be reused here.

To influence the ordering of your plugin's controls in the "Control" drop-down menu, modify the `m_iOrder` variable. The default is 0; higher values place the controls higher in the menu. Negative values force controls to appear lower.

### getView()

Returns a `QWidget*` with the view GUI element of the plugin. If the plugin does not need a view, return a `Q_NULLPTR` (null pointer) instead.

In all cases in MNE Analyze the view is created elsewhere and returned here as an instance. Many view widgets are already implemented in the Disp library and can be reused.

### getEventSubscriptions()

Returns a `QVector<EVENT_TYPE>` with the events the plugin is subscribed to. If the plugin does not care about incoming events, return an empty `QVector`. See example below:

```cpp
QVector<EVENT_TYPE> temp;
temp.push_back(SELECTED_MODEL_CHANGED);
temp.push_back(FILTER_ACTIVE_CHANGED);
temp.push_back(FILTER_DESIGN_CHANGED);
```

### handleEvent()

Receives event `e` that the plugin is subscribed to. Typically this is handled by a switch-case for each subscribed event:

```cpp
switch (e->getType()) {
    case EVENT_TYPE::SELECTED_MODEL_CHANGED:
        // do something
        break;
    case FILTER_ACTIVE_CHANGED:
        // do something
        break;
    case FILTER_DESIGN_CHANGED:
        // do something
        break;
    default:
        qWarning() << "[Averaging::handleEvent] Received an Event that is not handled by switch cases.";
}
```

## Views and Controls

Existing views and controls live in the Disp library and can be reused when building your plugin's GUI. Custom widgets can be created with Qt Designer (`.ui` files). All views and controls subclass `AbstractView`.

### Using the Event System

Events are how MNE Analyze plugins communicate. Subscribe to events to receive notifications about loaded data, selected items, or scaling changes. Send events to broadcast data or trigger actions in other plugins. See the [Event System](analyze-event.mdx) page for details.

### Setting and Clearing Data

Data is displayed using views that present the contents of models, following Qt's [Model/View](https://doc.qt.io/qt-6/model-view-programming.html) architecture.

## Static Building

When building statically, MNE Analyze needs to be told about the plugins at build time. To do this, the plugins are included with `Q_IMPORT_PLUGIN` in `applications/mne_analyze/mne_analyze/main.cpp` under the `#ifdef STATICBUILD` macro. The CMake configuration handles the rest of the static build setup automatically.

## Checklist

Before submitting your plugin as a pull request, verify:

- [ ] Plugin added to the `CMakeLists.txt` in the plugins directory
- [ ] Plugin's own `CMakeLists.txt` has correct target name, source files, and linked libraries
- [ ] Plugin has a `*_global.h` file with export macros
- [ ] All `AbstractPlugin` virtual functions are implemented
- [ ] `Q_IMPORT_PLUGIN` added in `main.cpp` for static builds
- [ ] Plugin compiles and loads successfully in MNE Analyze

## Useful References

- [Qt Layout Management](https://doc.qt.io/qt-6/layout.html)
- [Basics of User Interface Design](https://www.usability.gov/what-and-why/user-interface-design.html)
