Introduction
TDL scripts may define an arbitrary set of "TDL options". A TDL option is a holy trinity of:
The option variable. This is usually a Python variable in the global scope of a module, but it can also be a class member, or an item in a dict.
An entry in a .tdl.conf file. When a script is loaded, its option variables are initialized from the conf file.
An item in the "TDL Options" or "TDL Exec" menu of the browser. When the user changes an option via the menu, the option variable is automatically updated, and the new value is saved to .tdl.conf.
There are two kinds of TDL options:
Compile-time options show up in the "TDL Options" menu. This is for options that need to be set before a script is compiled (i.e. whatever is used by _define_forest); these options usually affect the structure of the tree. The script always needs to be recompiled before changes in these options take effect.
- Run-time options show up in the "TDL Exec" menu. This is for options that do not affect the tree per se, but are only used inside of TDL Jobs. The script does not need to be recompiled.
It is very easy to add options to your script. Just insert something like the following at global level:
1 TDLCompileOption('some_value',"This is a float option",[0,5,10],more=float); 2 TDLCompileOption('some_flag',"This is a boolean option",False); 3 TDLRuntimeOption('some_string',"This is a string option",["a","b"],more=str); 4 TDLRuntimeOption('some_filename',"This is a filename option",TDLFileSelect("*.txt")); 5 TDLRuntimeOption('some_directory',"This is a directory option",TDLDirSelect("*.MS"));
From this point on, the global variables of your script will include some_value, some_flag, and some_string. You script can then make use of them inside _define_forest and inside of your TDL jobs. If you load up your script in the browser, you will see corresponding items in the "TDL Options" and "TDL Exec" menus. When you change options, they will be written out to .tdl.conf, and reused the next time you run your script.
Declaring options
This is a very brief description, more details about declaring options are here: TdlOptions/DeclaringOptions.
The TDLCompileOption() and TDLRuntimeOption() functions require at least three arguments:
- The name of the option variable
- A short description of the option. This is used in menus.
- An option value, which determines the option type.
At the moment there are basically only three types of options:
- Booleans. A boolean value is passed as the third argument.
- Lists. A list of possible values is passed as the third argument. If you add a more=str, more=int or more=float keyword, the user will be able to enter additional values via the menu. Note that lists can contain values of any Python type. This can be, e.g., a list of filenames (possibly generated on-the-fly), or a list of functions.
File or directory selector. Pass in a TDLDirSelect() or TDLFileSelect() object as the third value.
Note that all calls to TDLCompileOption()s must be invoked at the global level of your script. This is because all compile-time options must be set up as soon as the script is imported. TDLRuntimeOptions() are usually at the global level, but can also be called from within _define_forest(). This is handy when the available set of runtime options depends on what compile-time options are selected.
More details about declaring options: TdlOptions/DeclaringOptions.
Mandatory vs. non-mandatory options
TDLCompileOption() may be invoked with a mandatory=True argument to declare a mandatory option. Mandatory options must all be set to some legal (i.e. not 'None') value before a script can be compiled. An example of this is the measurement set pathname.
The browser will disable the "Compile" button until all mandatory options have been set.
Runtime options cannot be declared mandatory.
Option sub-menus
When lots of options are used, things get confusing for the user. It is then very useful to group the options into sub-menus. This can be done as follows:
1 TDLCompileMenu("My sub-menu", 2 TDLOption('some_value',"This is a float option",[0,5,10],more=float), 3 TDLOption('some_flag',"This is a boolean option",False) 4 ); 5 TDLRuntimeMenu("Another sub-menu", 6 TDLOption('some_string',"This is a string option",["a","b"],more=str) 7 );
This groups the options into sub-menus. Note that calling TDLOption() is equivalent to TDLRuntimeOption() or TDLCompileOption(); since we do it inside a menu, there's no longer any need to specify whether the option is run-time or compile-time. In fact, you can still use TDLRuntimeOption() and TDLCompileOption() inside a menu:
1 TDLCompileMenu("My sub-menu", 2 TDLCompileOption('some_value',"This is a float option",[0,5,10],more=float), 3 TDLRuntimeOption('some_flag',"This is a boolean option",False) 4 );
...which is equivalent to just using TDLOption(). The "compile" or "runtime" property is anyway overridden by the "compile" or "runtime" property of the parent menu.
You can also nest menus inside menus via TDLMenu():
1 TDLCompileMenu("My sub-menu", 2 TDLOption('some_value',"This is a float option",[0,5,10],more=float), 3 TDLMenu("A sub-sub-menu", 4 TDLOption('some_flag',"This is a boolean option",False) 5 ) 6 );
You can prettify your menus with separators. Just pass in a None anywhere you want a separator:
1 TDLCompileMenu("My sub-menu", 2 TDLOption('some_value',"This is a float option",[0,5,10],more=float), 3 None, 4 TDLOption('some_flag',"This is a boolean option",False) 5 );
If you want to add separators at the "global" level, you can do it by invoking TDLCompileOptionSeparator() and TDLRuntimeOptionSeparator():
1 TDLCompileOption('some_value',"This is a float option",[0,5,10],more=float); 2 TDLCompileOptionSeparator(); 3 TDLCompileOption('some_flag',"This is a boolean option",False); 4 5 TDLRuntimeOption('some_string',"This is a string option",["a","b"],more=str); 6 TDLRuntimeOptionSeparator(); 7 TDLRuntimeOption('another_string',"This is a string option",["a","b"],more=str);
Submenu toggles
You can declare a submenu that is effectively a combination of a menu and a boolean option. This is done with the toggle= keyword:
1 TDLRuntimeMenu("Select channel subset", 2 TDLOption('start_chan',"First channel",[0],more=int), 3 TDLOption('end_chan',"Last channel",[0],more=int), 4 toggle="select_channels")
This will be presented to the user as a submenu with a checkbox next to it. The checkbox is meant to enable channel selection as a whole, while the items inside the submenu provide details on the selection. The state of the checkbox is stored in the select_channels variable.
TDL Jobs
By default, all global-level functions called _test_forest and _tdl_jobxxx are treated as "TDL jobs" and placed into the "TDL Exec" menu. It is possible to explicitly declare TDL jobs with different names:
1 def my_tdljob (mqs,parent,**kw); 2 # do something 3 4 TDLRuntimeJob(my_tdljob,"My own TDL job");
You can also invoke TDLRuntimeJob() (or, equivalently, TDLJob()) inside a TDLMenu, to place your jobs inside a sub-menu.
Options in imported modules
If you import a module that declares its own set of TDL options, they will automatically show up in the menus. This is quite convenient, but can lead to overcrowded menus, especially if you pull in several different modules. Fortunately, it is quite easy to stuff all of a module's options into a sub-menu:
1 import some_module 2 TDLCompileMenu("Options for 'some module'",some_module); 3 TDLRuntimeMenu("Runtime options for 'some module'",some_module);
That is, if you pass a module as an argument to TDLMenu(), all the options of that module are moved from the global level to inside that submenu. This is actually very powerful --- modules can declare all their own options "globally" without worrying about anyone else; when we import these modules, we make the decision where to put these options.
Note that inside .tdl.conf, option names will include the module name. For example, when importing some_module into myscript.py, you will see something like the following in .tdl.conf:
[myscript] some_value = 0 some_flag = 1 some_string = a some_module.some_option = some_value
This ensures that there are no naming conflicts, even when different modules use the same option names internally. NB: earlier implementations (prior to 07/06/2007) did not use module names inside config files.
Using option lists
A variation on the "module options" theme is option lists. For example, the Meow.Utils module provides an imaging_options() function, which returns a list of options related to imaging. You can then choose to place these options into the top-level menu:
1 TDLRuntimeOptions(*Meow.Utils.imaging_options(...));
...or into a sub-menu:
1 TDLRuntimeMenu("Imaging options",*Meow.Utils.imaging_options(...));
The TDLRuntimeOptions() and TDLCompileOptions() (note the plural) should be invoked with a number of options as arguments (we use the *-syntax here to specify arguments in a list). The options are then placed into the corresponding top-level menu. The TDLRuntimeMenu() function was already discussed above.
This approach --- returning lists of options, as opposed to declaring the options directly --- is useful if your module defines a number of optional features. If Meow.Utils declared its imaging options directly, they would show up in your menus every time you imported Meow.Utils, whether you wanted to include imaging or not. This way, the options only show up if you ask them to.
Smart options
You can have your options interact with each other. For example, it is possible to show and hide some options based on the values of other options. More details here: ./SmartOptions.
The .tdl.conf file
When using the browser, all (non-default) option settings are saved in a file called .tdl.conf in the current directory. From the example above, you can see that this file has quite a simple structure. You can make copies of config files, check them into svn, etc. It is also useful to make different versions of the config file for batch processing, since in batch mode a script is free to specify an alternative config filename.
NB: earlier implementations (prior to 07/06/2007) used the full filename of the script for their section names. The section above would have been named:
[/some/path/name/myscript.py] This of course added needless difficulties when, e.g., moving config files to different directories. Newer implementations just use the base filename of the script, without path and extension. To ease migration, the browser will check if an old-style config file is being used, and change the section name automatically.
