User Reference:Custom GUI Commands
BCI2000's Operator Module Scripting capabilities include the ability to prompt the user for specific pieces of information via modal Qt dialogs.
CHOOSE ...
The CHOOSE family of commands prompt the user to specify file or directory paths, via the Qt graphical user interface.
CHOOSE INPUT FILE(or equivalentlyCHOOSE FILE, orCHOOSE INPUT)- Prompts the user to select a single existing file, and prints the resulting file path.
CHOOSE INPUT FILES(or equivalentlyCHOOSE FILES)- Prompts the user to select one or more existing files, and prints the resulting file paths one per line (
FOR ... IN ...can iterate over these).
CHOOSE OUTPUT FILE(or equivalentlyCHOOSE OUTPUT)- Prompts the user to specify a file into which data can be saved (with an overwrite confirmation dialog if it already exists) and prints the resulting file path.
CHOOSE DIRECTORY- Prompts the user to specify a directory, and prints the resulting directory path.
If the user cancels the dialog, the commands print nothing.
The commands may be followed by one or more of the following optional clauses (they must appear in this order):
[OF] TYPE[S] <.ext1> [<.ext2> ...]- This limits the view (via a drop-down file-type selector) to files that have the specified extension(s). This clause cannot be used with
CHOOSE DIRECTORY. Instead of a file extension (starting with a dot) you can alternatively specify a glob pattern (containing a star). You can include*(or equivalently*.*) in the list if you want to give the user the option of overriding the limitation.
[STARTING] AT <dir>(equivalently you can sayINorFROMif it reads better)- This specifies where the dialog's file-system navigation should start (by default, it will start in the current working directory).
[WITH] PROMPT <msg>- This specifies the text prompt at the top of the dialog.
Examples:
choose file of type .dat from ../data/samplefiles with prompt "Choose a data file for playback:" choose output file of type .prm in ../parms with prompt "Save parameters as..." choose directory starting at ../data with prompt "Select a subject directory:"
CUSTOM DIALOG ...
This command assembles a flexibly customizable modal dialog for communicating with the user. The command returns (prints) a string denoting which button the user pressed to complete the dialog, and as a side effect it may set any environment variables that the command specifies. If the user cancels the dialog, the command does nothing (prints nothing, does not change any environment variables).
The CUSTOM DIALOG command can be followed by as many clauses as you like. The main types of clause are MESSAGE and VAR clauses, which are rendered from top to bottom in the dialog, in the order you provide. Possible clauses are:
MESSAGE <msg>- Each message is rendered as text, word-wrapped to the width of the dialog window.
MESSAGE ""can be used to create blank vertical space. Any message that consists only of dashes (e.g.MESSAGE ---) will be turned into a horizontal rule. Simplest example:custom dialog message "Hello world!"
VAR <variableName> <visibleLabel> {} [DEFAULT <defaultText>] [MINIMUM <nChars>] [CATALOG <listVariableName>]- This creates a text field that the user can fill in. If you supply a
DEFAULTsubclause, the specified default text is pre-filled. If you specify aMINIMUMnumber of characters, the dialog buttons (except for the designatedESCAPEbutton) will be disabled until this text field contains at least that many characters. If the dialog is completed (by any button other than the designatedESCAPEbutton) then the environment variable designated by<variableName>will be set to the contents of the text field. If you supply aCATALOGsubclause, the variable name will be added to a newline-delimited sequence of names stored in another environment variable, designated by<listVariableName>- this can be useful for later iteration (see below). A simpleVARexample:custom dialog var subjectName "Subject Name:" {} default test minimum 1
VAR <variableName> <visibleLabel> {<opt1>|<opt2>|...} [DEFAULT <defaultOption>] [MINIMUM <nChars>] [CATALOG <listVariableName>]- This is almost the same as the text-field
VARexample above, except that the braces are not empty: the|-delimited option labels inside the braces are then rendered as a drop-down menu with the specified limited set of options. TheDEFAULTsubclause, if provided, determines which option is pre-selected (if it is omitted, or the<defaultOption>does not match any of the options, then the first option is pre-selected). TheMINIMUMsubclause can be used, in conjunction with an empty first/default option, to force the user to make an explicit selection, as in the following example (note the leading|character inside the braces):custom dialog var runType "Run Type:" {|Calibration|Free Spelling} minimum 1
BUTTONS {<button1>|<button2>|...} [DEFAULT <defaultButton>] [ESCAPE <escapeButton>]- This explicitly overrides the sequence of buttons at the bottom of the dialog. The button label designated as
DEFAULT, if any, will be the one that gets highlighted by default and bound to the enter/return keystroke. If you do not specify a default, then "Ok" will be assumed to be the default button. If the designated default does not appear in the list of buttons, the buttons will all have equal status with no return-keystroke binding. The button label designated asESCAPE, if any, is the one that is bound to the escape keystroke, and pressing this button is equivalent to closing the dialog without making a selection (theCUSTOM DIALOGcommand will return the empty string in that case, and no environment variables will be touched). For most purposes, you will probably not need to override the button configuration explicitly. In dialogs withoutVARclauses (where you are simply displaying messages to the user), theBUTTONS {Ok} DEFAULT Okconfiguration is assumed. In dialogs with one or moreVARclauses (where you are asking the user for information), theBUTTONS {cancel|Ok} DEFAULT Ok ESCAPE cancelconfiguration is assumed.
TITLE <text>- This overrides the default dialog title "BCI2000".
SPACING <N>- Supply an integer value
Nto specify the vertical spacing between successiveMESSAGEand/orVARelements.
WIDTH <N>- Supply an integer value
Nto specify the minimum width of the dialog in pixels.
HEIGHT <N>- Supply an integer value
Nto specify the minimum height of the dialog in pixels.
LEFT <N>- Supply an integer value
Nto specify the horizontal coordinate of the dialog's left edge, in pixels.
TOP <N>- Supply an integer value
Nto specify the vertical coordinate of the dialog's top edge, in pixels.
CACHEFILE <FILENAME>- Specifying a cache file allows dialog contents to be made persistent across sessions. If the specified file already exists when the dialog is invoked, it will get run via
EXECUTE SCRIPT; if this sets any of the dialog's environment variables, then these new values override theDEFAULTvalues for the corresponding text fields and drop-down menus. But you do not need to initialize the specified file manually—if it does not exist, nothing is loaded. When the dialog is successfully completed (by any button other than the designatedESCAPEbutton),WRITE ENVIRONMENTis automatically called to write the correspondingSET ENVIRONMENTstatements to the specified file. So, when the same dialog is invoked next time around, these settings will be loaded: the user will see whatever settings they entered/chose this time. Example:custom dialog cachefile vars.bci var subjectName "Subject ID:" {} default Nobody minimum 1
Integrated CUSTOM DIALOG example
The following partial listing illustrates one way CUSTOM DIALOG might be used to implement a simplified launcher GUI, for a hypothetical auditory BCI experiment.
# [initialize system]
# [start up modules]
# [load baseline parameters for this BCI]
set environment launcherAction ${
custom dialog
title "Auditory BCI Launcher"
cachefile "${parent directory $0}/launcherDialogVars.bci"
var subjectName "Subject ID:" {} default "$subjectName" minimum 1
var runType "Run Type:" {Calibration|Free Selection}
var condition "Stimulus Condition:" {SlowStimuli.prm|FastStimuli.prm}
message "NB: each condition should be calibrated separately."
buttons {Quit|Launch} default Launch escape Quit
}
if [ $launcherAction == "" ]
quit
end
if [ $runType == Calibration ]
load parameterfile ../parms/calib.prm # let's assume you have created this
else
load parameterfile ../parms/free.prm # and this
end
load parameterfile ../parms/$stimulusCondition # and these
set parameter SubjectName $subjectName
setconfig
show window # unless you have some other remote-control mechanism for suspending/resuming runs
set state Running 1
The resulting dialog looks like this:
Example of CATALOG usage
The CATALOG option allows you to keep track of one or more lists of variable names. It helps to cut down on boilerplate repetition in certain common patterns. Here is an example:
# Create some new parameters
add parameter Application:Custom%20Stimuli float MinStimulusGap= 100ms 100ms 0 % // minimum gap to use in custom stimulus generation
add parameter Application:Custom%20Stimuli float MaxStimulusGap= 400ms 400ms 0 % // maximum gap to use in custom stimulus generation
# In the dialog, some entries will be environment variables with exactly
# the same names as parameters - let's catalog these
if [ ${
custom dialog
var SubjectName "Subject:" {} minimum 1 default "Nobody" catalog DialogParams
var MaxStimulusGap "Minimum Gap:" {0ms|50ms|100ms|200ms|400ms} default "100ms" catalog DialogParams
var MinStimulusGap "Maximum Gap:" {0ms|50ms|100ms|200ms|400ms} default "400ms" catalog DialogParams
var RunType "Run Type:" {Calibration.prm|FreeSpelling.prm}
cachefile "${parent directory $0}/launcherDialogVars.bci"
} == "" ]; quit; end
# ...
# The catalog saves us from having to hard-code a list of the new parameter names a third time:
for var in $DialogParams
set parameter $var ${$var}
# We chose the environment variable names to be the same as the corresponding parameter names.
end
# This dialog variable is handled differently, so we did not catalog it with the others:
load parameterfile $RunType
Notes: In the example above, we have used just one catalog variable, DialogParams, but you are free to use more than one if you want to assemble separate lists of variables for different purposes. The CUSTOM DIALOG command clears each catalog variable the first time it uses it (so, you can run the example above multiple times, and DialogParams will still only contain one copy of each name). Catalog variables get cleared and (re-)filled regardless of whether the dialog is accepted or cancelled.
Using HIDDEN (a.k.a. visible=false) and FIXED (a.k.a. fixed=true) to make a "wizard"
Each VAR entry may also have a modifier that overrides its visibility and whether the user can actually change it.
- Use
HIDDENorINVISIBLEorvisible=falseto hide a particularVARfrom the user's view (but it will still set its environment variable if the user accepts the dialog). The opposite,VISIBLEorvisible=true, makes theVARvisible in the GUI (which is the default behavior anyway). - Use
FIXEDorfixed=trueto display theVAR's label and value but without giving the user the chance to change it (and it will still set its environment variable if the user accepts the dialog). The opposite,fixed=false, makes theVARadjustable (which is the default behavior anyway).
The ability to make certain variables fixed or hidden, while still including them in the GUI, allows the de-facto construction of multi-stage "wizards" using repeated calls to CUSTOM DIALOG. The following example allows the operator to configure the task as "active" (which then makes a certain subset of parameters visible for configuration in the second stage) or "passive" (associated with a different subset of parameters).
# "wizard" example leveraging `visible=false` and `fixed=true` modifiers
add parameter Application:Robot string TaskVariant= Active % % % // None or Passive or Active
add parameter Application:Robot float ActivePulseDuration= 100ms % 50ms % //
add parameter Application:Robot float ActivePulseISI= 100ms % 10ms % //
add parameter Application:Robot float PassiveStandardDuration= 100ms % 50ms % //
add parameter Application:Robot float PassiveDurationDelta= 100ms % 10ms % //
wizardStage := 1
set variable response "-"
while [ $response != Launch ] && [ $response != "" ] # NB: QUIT will not succeed if placed inside the WHILE loop, and there is no BREAK
if [ $wizardStage == 1 ]
set environment TaskVariant ""
set variable buttons {cancel|Next}
set variable defaultButton Next
elseif [ $wizardStage == 2 ]
set variable buttons {cancel|Previous|Launch}
set variable defaultButton Launch
end
set variable response ${
custom dialog
var SUBJECTNAME "Subject:" {}
default "Nobody" minimum 1
var TaskVariant "Task Variant:" {None|Passive|Active}
default "Active" minimum 1 catalog DialogParams fixed=${[ $wizardStage != 1 ]}
var PassiveStandardDuration "Passive Standard Duration:" {50ms|100ms|150ms|200ms|250ms}
default "100ms" minimum 1 catalog DialogParams visible=${[ $TaskVariant == "Passive" ]}
var PassiveDurationDelta "Passive Duration Delta:" {0ms|10ms|20ms|30ms|40ms|50ms|60ms|70ms|80ms|90ms|100ms}
default "100ms" minimum 1 catalog DialogParams visible=${[ $TaskVariant == "Passive" ]}
var ActivePulseDuration "Active Pulse Duration:" {50ms|100ms|150ms|200ms|250ms}
default "100ms" minimum 1 catalog DialogParams visible=${[ $TaskVariant == "Active" ]}
var ActivePulseISI "Active Pulse ISI:" {0ms|10ms|20ms|30ms|40ms|50ms|60ms|70ms|80ms|90ms|100ms}
default "100ms" minimum 1 catalog DialogParams visible=${[ $TaskVariant == "Active" ]}
message "(no task-specific parameters)" visible=${[ $task == None ]}
top 200
width 400
cachefile "${parent directory $0}/launcherDialogVars.bci"
buttons $buttons default $defaultButton escape cancel
}
if [ $response == "Previous" ]; wizardStage := wizardStage - 1; end
if [ $response == "Next" ]; wizardStage := wizardStage + 1; end
end
if [ $response == "" ]; quit; end
for x in $DialogParams
set parameter $x ${$x}
end
