Skip to main content

Event System

This guide covers the event system used in MNE Analyze for inter-plugin communication, not to be confused with the Event Manager plugin.

Overview

In addition to the Qt Framework's signal/slot system, MNE Analyze uses a dedicated event system for all communication between plugins. This event system is integrated into all plugins through the AbstractPlugin interface, allowing every plugin to send and receive events. The event manager runs on a separate thread, cycling through a buffer and delivering queued events to subscribed plugins.

Events

Events can be used to send data, trigger things, or both. As an example: SELECTED_MODEL_CHANGED, one of the most widely used event types, which is triggered by the selection of a new item in the Data Manager, contains a QSharedPointer<ANSHAREDLIB::AbstractModel>>, a pointer to the selected data item; while TRIGGER_REDRAW, used for making the Signal Viewer update, contains no data.

All event types are declared in applications/mne_analyze/libs/anShared/Utils/types.h in the EVENT_TYPE enum. To add a new event type, simply add an entry to this enum. Events should ideally serve a single purpose and always send and expect the same type of data, if any.

Sending Events

Events are sent using the Communicator class (applications/mne_analyze/libs/anShared/Management/communicator.h) via its publishEvent() method. When sending an event you pass an event type and, optionally, data wrapped in a QVariant. Below is a code snippet of the Filtering plugin broadcasting an event with data:

void Filtering::setFilterChannelType(const QString& sType)
{
QVariant data;
data.setValue(sType);
m_pCommu->publishEvent(EVENT_TYPE::FILTER_CHANNEL_TYPE_CHANGED, data);
}

The string sType is stored in the QVariant via setValue() before being passed along with the event.

Receiving Events

To receive events of a certain type, a plugin must subscribe to that type. This is done in the plugin's implementation of getEventSubscriptions() by returning a vector of all the event types the plugin is interested in. Below is a code snippet of the Averaging plugin subscribing to events:

QVector<EVENT_TYPE> Averaging::getEventSubscriptions(void) const
{
QVector<EVENT_TYPE> temp;
temp.push_back(SELECTED_MODEL_CHANGED);
temp.push_back(FILTER_ACTIVE_CHANGED);
temp.push_back(FILTER_DESIGN_CHANGED);
temp.push_back(EVENT_GROUPS_UPDATED);
temp.push_back(CHANNEL_SELECTION_ITEMS);
temp.push_back(SCALING_MAP_CHANGED);
temp.push_back(VIEW_SETTINGS_CHANGED);

return temp;
}

Once subscribed, plugins handle incoming events via handleEvent(). Below is how the AnnotationManager plugin handles its events:

void AnnotationManager::handleEvent(QSharedPointer<Event> e)
{
switch (e->getType()) {
case EVENT_TYPE::NEW_ANNOTATION_ADDED:
emit newAnnotationAvailable(e->getData().toInt());
onTriggerRedraw();
break;
case EVENT_TYPE::SELECTED_MODEL_CHANGED:
onModelChanged(e->getData().value<QSharedPointer<ANSHAREDLIB::AbstractModel> >());
break;
default:
qWarning() << "[AnnotationManager::handleEvent] Received an Event that is not handled by switch cases.";
}
}

Best Practices

  • Delegate to helper functions. Unless the handling code is very short, move the logic into a dedicated method for readability. See the onModelChanged() pattern used in plugins that receive SELECTED_MODEL_CHANGED.
  • One purpose per event. Each event type should carry a consistent data payload and serve a single, well-defined purpose.
  • Document your events. When introducing a new EVENT_TYPE, add a brief comment in types.h describing what data it carries and which plugins produce/consume it.

Common Event Types

EventData PayloadTypical Use
SELECTED_MODEL_CHANGEDQSharedPointer<AbstractModel>A new item was selected in the Data Manager
FILTER_ACTIVE_CHANGEDboolFilter was toggled on/off
FILTER_DESIGN_CHANGEDFilter parametersFilter settings were modified
TRIGGER_REDRAW(none)Request views to repaint
SCALING_MAP_CHANGEDScaling mapChannel scaling was updated
NEW_ANNOTATION_ADDEDint (annotation index)A new annotation was created