Jump to content

User Reference:Custom GUI Commands

From BCI2000 Wiki

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 equivalently CHOOSE FILE, or CHOOSE 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 say IN or FROM if 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 DEFAULT subclause, the specified default text is pre-filled. If you specify a MINIMUM number of characters, the dialog buttons (except for the designated ESCAPE button) will be disabled until this text field contains at least that many characters. If the dialog is completed (by any button other than the designated ESCAPE button) then the environment variable designated by <variableName> will be set to the contents of the text field. If you supply a CATALOG subclause, 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 simple VAR example:
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 VAR example 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. The DEFAULT subclause, 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). The MINIMUM subclause 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 as ESCAPE, 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 (the CUSTOM DIALOG command 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 without VAR clauses (where you are simply displaying messages to the user), the BUTTONS {Ok} DEFAULT Ok configuration is assumed. In dialogs with one or more VAR clauses (where you are asking the user for information), the BUTTONS {cancel|Ok} DEFAULT Ok ESCAPE cancel configuration is assumed.
TITLE <text>
This overrides the default dialog title "BCI2000".
SPACING <N>
Supply an integer value N to specify the vertical spacing between successive MESSAGE and/or VAR elements.
WIDTH <N>
Supply an integer value N to specify the minimum width of the dialog in pixels.
HEIGHT <N>
Supply an integer value N to specify the minimum height of the dialog in pixels.
LEFT <N>
Supply an integer value N to specify the horizontal coordinate of the dialog's left edge, in pixels.
TOP <N>
Supply an integer value N to 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 the DEFAULT values 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 designated ESCAPE button), WRITE ENVIRONMENT is automatically called to write the corresponding SET ENVIRONMENT statements 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 HIDDEN or INVISIBLE or visible=false to hide a particular VAR from the user's view (but it will still set its environment variable if the user accepts the dialog). The opposite, VISIBLE or visible=true, makes the VAR visible in the GUI (which is the default behavior anyway).
  • Use FIXED or fixed=true to display the VAR'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 the VAR adjustable (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