A07: Contributing to EEGLAB
EEGLAB operates in the very rich Matlab environment. The structure of EEGLAB functions makes it easy to combine them in new ways in original Matlab scripts which may use any of the wide variety of processing tools and methods available in Matlab. Thus, the most straightforward way to add to EEGLAB capabilities is to use EEGLAB functions and data structures in your own Matlab scripts, something many or most EEGLAB users do routinely.
Matlab functions provide a level of encapsulation and isolation that minimizes the possibility of interference with variable names and processing outside the functions themselves, e.g. in Matlab scripts that call them. EEGLAB is, in essence, a large set of Matlab functions. Please send us (eeglab@sccn.ucsd.edu) any EEGLAB-compatible functions you think would be of interest to other researchers. We will consider any functions sent us for inclusion in the EEGLAB distribution. Include a succinct explanation and references for the signal processing methods used. If possible, use EEGLAB help message style.
Contents |
Open source policy
- EEGLAB is distributed under the GPL GNU license, which states that the software cannot be modified for commercial purposes. Any contributed functions we add to EEGLAB will be made available for free non-commercial use under this license.
- We will credit your authorship of the functions on the function help pages. The authors will retain all commercial rights to the functions.
- Functions that we find to be accurate, stable, of some general interest and complementary to existing EEGLAB functions will first be included in a 'contributed' directory distributed as a part of EEGLAB. If a contributed function proves to be stable and of widespread interest, we may integrate it into the main EEGLAB menu.
Also consider the 'plug-in' option (described in Section below). EEGLAB _plug-ins_ allow authors to flexibly incorporate new functions in the EEGLAB menu of all users who have also downloaded their plug-in.
How to write EEGLAB functions
Adding new functionality to EEGLAB requires a pair of functions, a signal processing function (Ex: sample.m) and an accompanying pop_function (Ex: pop_sample.m). The pop_function pops up a text input window allowing the user to specify arguments to the signal processing function. The Matlab help messages for each function should state clearly what input arguments the functions require and what they output, using the help message format explained below. You should read the beginning of the script section to understand the different levels of functions in EEGLAB.
The signal processing function
This function should comply with the EEGLAB help-message syntax to allow the header to be converted into an .html help page by (functions makehtml() and help2html(). We propose this sample header. Below is the GNU license disclaimer to include in the function header.
This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA
The associated pop_ function
The pop_function creates a graphic interface to the signal processing function. It should include the same disclaimer as the signal processing function and
- It must take the EEGLAB data structure 'EEG' as a first input. The second parameter may specify whether the signal processing function will be applied to ICA component activations or to the raw EEG data channels.
- Additional parameters should be optional. If they are left blank in the pop_function call, a window should pop-up to ask for their values.
- The pop_function must return a 'string' containing the resulting call to the signal processing function (or in some cases to the pop_function). When this string is evaluated (using the Matlab function 'eval()'), the result must be the same as that of the pop_function itself, e.g. including all the parameters the user may enter into the pop window. This string will be pushed into the EEGLAB command history stack.
- By convention, if the function draws a figure, calling the function without enough arguments should pop up a new figure. However, with enough arguments (macro call), the function should directly draw the output graphic in the current figure (thus allowing the user to build multi-part figures, e.g. using 'sbplot()' commands). Writing the pop_function is easy and can usually be done in a few minutes if you just modify the pop_sample.m function source.
How to write an EEGLAB plug-in
EEGLAB also has a plug-in facility that automatically searches for functions loaded into a specified plug-in directory, causing them to appear in the EEGLAB menu. EEGLAB plugin functions can be offered for download and use by anyone, independent of the EEGLAB distribution, or by mutual agreement with the EEGLAB developers may be included in the EEGLAB distribution itself.
EEGLAB will automatically incorporate any appropriately named "plugin" functions (Ex: 'eegplugin_myfunc.m') that EEGLAB finds in the same directory as 'eeglab.m'. Creating an EEGLAB plugin function in this location will add a menu item with the menu label(s) specified in your plugin function to the bottom of an EEGLAB menu (the top menu label possibly linked to an unlimited number of sub-menus). These menu item(s) can call standard or custom data processing and "pop" functions (see examples below). When a user downloads an EEGLAB plugin (either from the main EEGLAB site or from any other site), he or she simply has to uncompress the plugin function files into the plugin function sub-directory or into the main EEGLAB directory (in which eeglab.m is located). The plugin function will then be detected by EEGLAB at startup by looking for files or directories with names beginning with eegplugin_ in the main EEGLAB directory (i.e., in the same directory as 'eeglab.m'). You may also place this file in a sub-directory of the EEGLAB plugin function directory.
The plugin function
To create a new EEGLAB plug-in, simply create a Matlab function file whose name begins with eegplugin_ and place it in the plugin function subdirectory or in your main EEGLAB directory. This function must take three arguments, as in the 'test' plugin function shown below:
>> eegplugin_test (fig, try_strings, catch_strings);
The three arguments above are provided to the plugin function by eeglab(). The first argument ('fig') is the handle of the main EEGLAB window. The second and third arguments are structures passed by EEGLAB that allow the plugin function to check parameters, detect errors, etc. (see below). If you do not want your plug-in to alter EEGLAB history and error message handling, you can ignore the latter two parameters (although the plugin function definition still must list all three arguments).
To create a new sub-menu under a top-level EEGLAB menu, simply add a command like this to your plugin function:
>> uimenu( fig, 'label', 'My function', 'callback', ... [ 'EEG = pop_myfunc(EEG, ...); [ALLEEG EEG CURRENTSET] ... = eeg_store(ALLEEG, EEG, CURRENTSET);' ]);
The statement [ALLEEG EEG CURRENTSET] = eeg_store(ALLEEG, EEG, CURRENTSET); above insures that your modified EEG dataset will be stored in the EEGLAB ALLEEG structure.
Plug-ins and EEGLAB history
If you want your plug-in to interact with the EEGLAB history mechanism, you should take advantage of the second ('try_strings') and third ('catch_strings') arguments to your plugin function. The second argument (see eegplugin_test() above) contains commands (organized into a Matlab structure) that check the input dataset and attempt to execute your command. The third argument ('catch_strings') contains commands to handle errors and add the contents of the LASTCOM (i.e., last command) variable to the EEGLAB history.
Plugin functions should declare one or more EEGLAB menu items. Each menu declaration should look like this:
uimenu( submenu, 'label', 'My function', 'callback', ... [ try_strings.anyfield '[EEG LASTCOM] ... = pop_myfunc(EEG, ...);' arg3.anyfield ]);
Possible fields for 'try_strings' (above) are:
- try_strings.no_check : check for the presence of a non-empty EEG dataset only
- try_strings.check_ica : check that the dataset includes ICA weights
- try_strings.check_cont : check that the dataset is continuous
- try_strings.check_epoch : check that the dataset is epoched
- try_strings.check_event : check that the dataset contains events
- try_strings.check_epoch_ica : check that the dataset is epoched and includes ICA weights
- try_strings.check_chanlocs : check that the dataset contains a channel location file
- try_strings.check_epoch_chanlocs : check that the dataset is epoched and includes a channel locations (chanlocs) file
- try_strings.check_epoch_ica_chanlocs : check that the dataset is epoched and includes ICA weights and a channel locations file.
Possible fields for 'catch_strings' are:
- catch_strings.add_to_hist : add the LASTCOM variable content (if not empty) to the EEGLAB history
- catch_strings.store_and_hist : add the LASTCOM variable content (if not empty) to the EEGLAB history and store the EEG dataset in the ALLEEG variable.
- catch_strings.new_and_hist : add the LASTCOM variable content (if not empty) to the EEGLAB history and pop up a window for a new dataset.
Plugin function examples
A simplest type of plugin function might only call a plotting function. For instance, to write a simple plug-in to plot the ERP trial average at every channel in a different color (without performing any data checking):
% eegplugin_erp() - plot ERP plugin function eegplugin_erp( fig, try_strings, catch_strings); % create menu plotmenu = findobj(fig, 'tag', 'plot'); uimenu( plotmenu, 'label', 'ERP plugin', ...'callback', 'figure; plot(EEG.times, mean(EEG.data,3));');
Save the text above as a file, 'eegplugin_erp.m' into the plugin function sub-directory of EEGLAB (or the EEGLAB directory where _eeglab.m_ is located) and restart EEGLAB (click here to download this .m file). Then select the menu item Plot > ERP plugin to plot the ERP of an epoched dataset.
Another, more complete example: To create a plug-in named 'PCA' that would apply PCA to your data and store the PCA weights in place of the ICA weights, save the Matlab commands below as file 'eegplugin_pca.m' into the plugin sub-directory of EEGLAB (or the EEGLAB directory in which eeglab.m is located) and restart EEGLAB (click here to download this .m file).
% eegplugin_pca() - a pca plug-in function eegplugin_pca( fig, try_strings, catch_strings); % create menu toolsmenu = findobj(fig, 'tag', 'tools'); submenu = uimenu( toolsmenu, 'label', 'PCA plugin'); % build command for menu callback\\ cmd = [ '[tmp1 EEG.icawinv] = runpca(EEG.data(:,:));' ]; cmd = [ cmd 'EEG.icaweights = pinv(EEG.icawinv);' ]; cmd = [ cmd 'EEG.icasphere = eye(EEG.nbchan);' ]; cmd = [ cmd 'clear tmp1;' ]; finalcmd = [ try_strings.no_check cmd ]; finalcmd = [ finalcmd 'LASTCOM = ''' cmd ''';' ]; finalcmd = [ finalcmd catch_strings.store_and_hist ]; % add new submenu uimenu( submenu, 'label', 'Run PCA', 'callback', finalcmd);
Note: As of EEGLAB v4.3 you may use plugin functions to append new plug-in related menu items under different EEGLAB menu headings. Above, we add a sub-menu to the Tools menu by specifying 'tag','tools' in the findobj() call. If the specified tag were import data, EEGLAB would add the plug-in menu item to the File > Import data menu. Using the tag import epoch would add the plugin function menu item to the File > Import epoch info menu. The tag import event would add the plugin menu item to the File > Import event info menu. The tag export would add the plugin menu item to the File > Export data menu. Finally, the tag plot would add the plugin menu item to the Plot menu. (Note that the tag call should be in lower case).
After installing the plugin function above, a new EEGLAB menu item Tools > PCA plugin will be created. Use this menu item to run PCA on the current EEG dataset. The resulting PCA decomposition will be stored in place of the ICA decomposition. (Note: This is possible (if not typically advisable) since both PCA and ICA are linear decompositions).
See the EEGLAB DIPFIT plugin eegplugin_dipfit.m for an example of a more elaborate plug-in.
Note: In EEGLAB4.3 we slightly modified how EEGLAB handles plugin functions. As a result, later EEGLAB versions might not have been compatible with earlier plugin functions. Subsequent versions of EEGLAB have and will support backwards compatibility of the plug-in conventions.
As of EEGLAB11, it is now possible for the user to control menu behavior within EEGLAB. EEGLAB enables and disables menu items automatically based on data in memory (as shown to the user by greying the menu item text). This may result in some plug-in related menu items being automatically disabled under circumstances in which they should not be. In EEGLAB11, we have implemented a new plug-in menu item activation scheme that makes it possible to control when a plug-in's menu item(s) will be active. To do so, developers must use several keywords in the "userdata" field of the menu item:
| Keyword | Default | Description |
|---|---|---|
| epoch | on | set to off to disable the menu item when an epoched dataset is being processed |
| continuous | on | set to off to disable the menu item when a continuous dataset is being processed |
| startup | off | set to on to enable the menu item at start up when no data have been loaded |
| study | off | set to on to enable the menu item when a STUDY is being processed |
| chanloc | off | when set to on, the menu item will be disabled if channel locations are not present |
Keywords should be separated by semicolons. An example of a plug-in related menu item that is enabled only at startup and when processing EEGLAB studies is shown below:
toolsmenu = findobj(fig, 'tag', 'tools'); uimenu( submenu, 'label', 'My menu', 'callback', mycommand, 'userdata', 'startup:on;study:on');
Note that adding epoch:on or continuous:on would not change the menu item behavior since these are default behaviors. The following example would enable the menu item only when processing continuous or epoched datasets that have channel locations present:
toolsmenu = findobj(fig, 'tag', 'tools'); uimenu( submenu, 'label', 'My menu', 'callback', mycommand, 'userdata', 'chanloc:on');
Omitting the userdata parameter results in the plugin menu item adopting the default behaviors defined in the table above.
Using EEGLAB GUI design functions
The function supergui() is the main function used for creating graphic interfaces in EEGLAB. We will be describing below some basic and more advanced uses for it:
Using the supergui function
Supergui was designed to alleviate the burden of manually creating graphic interface for each function. Instead a series of controls are defined along with a geometry and the figure is automatically created. An example is shown below:
supergui( 'geomhoriz', { 1 1 1 }, 'uilist', { ... { 'style', 'text', 'string', 'Hello!' }, { }, ... { 'style', 'pushbutton' , 'string', 'OK' } } );
This creates the following graphic interface
The command line call above contains two main arguments: 'geometry' and 'uilist'. 'geometry' defines the geometry in this case 3 lines { 1 1 1} (see more complex geometry below). The 'uilist' defines the content for each of these lines. The first line contains some text "{ 'style', 'text', 'string', 'Hello!' }". The second line is empty "{ }" and the third line contains the "OK" button "{ 'style', 'pushbutton' , 'string', 'OK' }".
If you press OK, nothing will happen however because no action has been associated with this button. To associate an action to a UI control, simply use the 'callback' command and enter any Matlab command in the associated string parameter.
supergui( 'geomhoriz', { 1 1 1 }, 'uilist', { ... { 'style', 'text', 'string', 'Hello!' }, { }, ... { 'style', 'pushbutton' , 'string', 'OK' 'callback' 'close(gcbf);' } } );
Above, we added the 'callback' command "close(gcbf)" which closes the current figure when users press the "OK" button.
Almost any geometry (but not all as detailed in the advanced section) may be defined using this scheme. For instance
supergui( 'geomhoriz', { [1 1] 1 1 }, 'uilist', { ... { 'style', 'text', 'string', 'Enter some text' }, ... { 'style', 'edit', 'string', 'Hello!' }, { }, ... { 'style', 'pushbutton' , 'string', 'OK' 'callback' 'close(gcbf);' } } );
The first line now has geometry [1 1] which means that two UI will share this line and have the same horizontal extent. Changing [1 1] to [1 0.5] as shown below forces the second UI on the line to have only half the horizontal extent of the first one.
In some case, it is convinient to have a UI spam more than one line of text. This is often the case for listbox for instance. Below we define a listbox with multiple choice and use the additional command 'geomvert' to specify the vertical geometry. It is set to [3 1 1] which means that the UI on the first line will span 3 lines of text, and the other lines will only span one line of text (we have to add carriage returns - character 10 - to the text of the first UI so this text is aligned with the listbox).
supergui( 'geomhoriz', { [1 1] 1 1 }, 'geomvert', [3 1 1], 'uilist', { ... { 'style', 'text', 'string', [ 'Make a choice' 10 10 10 ] }, ... { 'style', 'listbox', 'string', 'Choice 1|Choice 2|Choice 3' }, { }, ... { 'style', 'pushbutton' , 'string', 'OK' 'callback' 'close(gcbf);' } } );
Using the inputgui function
Although supergui can be useful, it only creates the GUI and all the control flow of events needs to be handled by the UIs; i.e., creating a button "OK", a button "Cancel" and handling the events associated to these buttons are left to the user. The inputgui() function (which calls the supergui function) helps supplement this by automatically creating these buttons and also by processing outputs. For instance:
res = inputgui( 'geometry', { [1 1] }, ... 'geomvert', [3], 'uilist', { ... { 'style', 'text', 'string', [ 'Make a choice' 10 10 10 ] }, ... { 'style', 'listbox', 'string', 'Choice 1|Choice 2|Choice 3' } } );
The "res" output contains the results in this case a cell array containing a single value from 1 to 3 indicating which choice the user made (for instance { [1] }). The number of elements in this cell array depends on the number of UIs returning output (in general text and button do not return output but checkbox, listbox, and edit box do). Another way to use this function is to set tag for each UI and use the 4th output
[res userdata err structout] = inputgui( 'geometry', { [1 1] }, ... 'geomvert', [3], 'uilist', { ... { 'style', 'text', 'string', [ 'Make a choice' 10 10 10 ] }, ... { 'style', 'listbox', 'string', 'Choice 1|Choice 2|Choice 3' 'tag' 'list' } } );
Now the output structure "structout" contains a field named "list" that will contain the output of the listbox (structout.list = 1 for instance). This is convenient for creating GUIs, as developers do not need to remember the order of the output. You may change the order of lines and you will not have to change any other part of your script. Note that this is not the case when using the "res" output described at the beginning of this section.
A complex UI shown below uses this technique. It is inspired by the pop_newtimef() function.
g = [1 0.3 0.6 0.34]; geometry = { g g g g g g g g [1.025 1.27] [1] [1.2 1 1.2]}; uilist = { ... { 'Style', 'text', 'string', 'Channel number', 'fontweight', 'bold' } ... { 'Style', 'edit', 'string', '1' 'tag' 'chan'} {} {} ... ... { 'Style', 'text', 'string', 'Time limits [min max] (msec)', 'fontweight', 'bold' } ... { 'Style', 'edit', 'string', ' ' 'tag' 'tlimits' } ... { 'Style', 'popupmenu', 'string', 'Use 50 time points|Use 100 time points' ... 'tag' 'ntimesout' 'value' 4} { } ... { 'Style', 'text', 'string', 'Frequency limits (Hz)', 'fontweight', 'bold' } ... { 'Style', 'edit', 'string', ' ' 'tag' 'freqs' } ... { 'Style', 'popupmenu', 'string', 'Use limits, padding 1|Use limits, padding 2' ... 'tag' 'nfreqs' } ... { 'Style', 'checkbox', 'string' 'Log spaced' 'value' 0 'tag' 'freqscale' } ... ... { 'Style', 'text', 'string', 'Baseline limits [min max] (msec) (0->pre-stim.)', ... 'fontweight', 'bold' } ... { 'Style', 'edit', 'string', '0' 'tag' 'baseline' } ... { 'Style', 'popupmenu', 'string', 'Use divisive baseline|Use standard deviation' ... 'tag' 'basenorm' } ... { 'Style', 'checkbox', 'string' 'No baseline' 'tag' 'nobase' } ... { 'Style', 'text', 'string', 'Wavelet cycles [min max/fact] or sequence', ... 'fontweight', 'bold' } ... { 'Style', 'edit', 'string', '3 0.5' 'tag' 'cycle' } ... { 'Style', 'popupmenu', 'string', 'Use limits|Use actual seq.' 'tag' 'ncycles' } ... { 'Style', 'checkbox', 'string' 'Use FFT' 'value' 0 'tag' 'fft' } ... { 'Style', 'text', 'string', 'ERSP color limits [max] (min=-max)', ... 'fontweight', 'bold' } ... { 'Style', 'edit', 'string', ' ' 'tag' 'erspmax'} ... { 'Style', 'checkbox', 'string' 'see log power (set)' 'tag' 'scale' 'value' 1} ... {} ... { 'Style', 'text', 'string', 'ITC color limits [max]', 'fontweight', 'bold' } ... { 'Style', 'edit', 'string', ' ' 'tag' 'itcmax'} ... { 'Style', 'checkbox', 'string' 'plot ITC phase (set)' 'tag' 'plotphase' } {} ... { 'Style', 'text', 'string', 'Bootstrap significance level (Ex: 0.01 -> 1%)', ... 'fontweight', 'bold' } ... { 'Style', 'edit', 'string', ' ' 'tag' 'alpha'} ... { 'Style', 'checkbox', 'string' 'FDR correct (set)' 'tag' 'fdr' } {} ... { 'Style', 'text', 'string', 'Optional newtimef() arguments (see Help)', ... 'fontweight', 'bold'} ... { 'Style', 'edit', 'string', ' ' 'tag' 'options' } ... {} ... { 'Style', 'checkbox', 'value', 1, 'string', ... 'Plot Event Related Spectral Power', 'tooltipstring', ... 'Plot log spectral perturbation image in the upper panel' 'tag' 'plotersp' } ... { 'Style', 'checkbox', 'value', 1, 'string', ... 'Plot Inter Trial Coherence', 'tooltipstring', ... 'Plot the inter-trial coherence image in the lower panel' 'tag' 'plotitc' } ... { 'Style', 'checkbox', 'value', 0, 'string', ... 'Plot curve at each frequency' 'tag' 'plotcurve' } ... }; [ tmp1 tmp2 strhalt structout ] = inputgui( geometry, uilist, ... 'pophelp(''pop_newtimef'');', 'Plot channel time frequency -- pop_newtimef()');
Copying and pasting the script above should generate the GUI below. Note how simply the geometry was defined. The supergui function adapts automatically the width and height of the window to the containing elements. Also note the "Help" button which executes the command "'pophelp(pop_newtimef);'" defined in the inputgui command line call. Upon pressing 'OK', the fields of the the "structout" structure contain all the user entry in the GUI.
Advanced GUI design
One limitation of the supergui calling format is that it is not possible to define random geometries. Any given line needs to span the same height. There is an advanced calling format that allows to circumvent that problem.
supergui('geom', { {2 2 [0 0] [1 2] } { 2 2 [1 0] [2 1] } { 2 2 [1 1] [2 2] } }, ... 'uilist', { { 'style' 'listbox' 'string' 'cell 1|cell 2' } ... { 'style' 'checkbox' 'string' 'cell 3' } ... { 'style' 'text' 'string' 'cell 4' } });
This type of interface is not possible using standard calls. The 'geom' input is detailed in the help section of the supergui function.






