Skip to content
Gijs Molenaar edited this page Feb 13, 2014 · 4 revisions

Smart Options

"Smart options" refers to the ability of options to be clever and interact with each other. For example, consider a module that provides different ionospheric models (e.g. Timba/doc/Courses/Workshop2007/ME2/iono_model.py). This declares the following set of options:

TDLCompileOption('iono_model',"Ionospheric model",[sine_tid_model,wedge_model]);TDLCompileOption('TEC0',"Base TEC value",[0,5,10],more=float);TDLCompileOptionSeparator();TDLCompileMenu('Sine TID model options',  TDLOption('tid_x_ampl_0',"Relative TID-X amplitude at t=0",[0,0.01,0.02,0.05,0.1],more=float),  TDLOption('tid_x_ampl_1hr',"Relative TID-X amplitude at t=1hr",[0,0.002,0.01,0.02,0.05,0.1],more=float),  TDLOption('tid_x_size_km',"TID-X size, in km",[25,50,100,200,1000],more=int),  TDLOption('tid_x_speed_kmh',"TID-X speed, in km/h",[25,50,100,200,300,500],more=int),  None,  TDLOption('tid_y_ampl_0',"Relative TID-Y amplitude at t=0",[0,0.01,0.02,0.05,0.1],more=float),  TDLOption('tid_y_ampl_1hr',"Relative TID-Y amplitude at t=1hr",[0,0.002,0.01,0.02,0.05,0.1],more=float),  TDLOption('tid_y_size_km',"TID-Y size, in km",[25,50,100,200,1000],more=int),  TDLOption('tid_y_speed_kmh',"TID-Y speed, in km/h",[25,50,100,200,300,500],more=int),);TDLCompileMenu('Wedge model options',  TDLOption('wedge_min','Min delta-TEC over 100km',[0,0.001,0.1,1,2,5],more=float),  TDLOption('wedge_max','Max delta-TEC over 100km',[0,0.001,0.1,1,2,5],more=float),  TDLOption('wedge_time','Time to reach max delta-TEC, hours',[1,2,4,8],more=float));```
This module provides two mutually exclusive models: sine-TID and wedge. The first compile-time option selects between the two models. Furthermore, each model provides its own set of options (here grouped in two sub-menus.) Things would be much less confusing for the user if the sub-menus were automatically enabled and disabled based on which model was selected. This is not hard to accomplish: 


```python
iono_model_option = TDLCompileOption('iono_model',"Ionospheric model",  [sine_tid_model,wedge_model]);TDLCompileOption('TEC0',"Base TEC value",[0,5,10],more=float);TDLCompileOptionSeparator();sine_options = TDLCompileMenu('Sine TID model options',  TDLOption('tid_x_ampl_0',"Relative TID-X amplitude at t=0",[0,0.01,0.02,0.05,0.1],more=float),  TDLOption('tid_x_ampl_1hr',"Relative TID-X amplitude at t=1hr",[0,0.002,0.01,0.02,0.05,0.1],more=float),  TDLOption('tid_x_size_km',"TID-X size, in km",[25,50,100,200,1000],more=int),  TDLOption('tid_x_speed_kmh',"TID-X speed, in km/h",[25,50,100,200,300,500],more=int),  None,  TDLOption('tid_y_ampl_0',"Relative TID-Y amplitude at t=0",[0,0.01,0.02,0.05,0.1],more=float),  TDLOption('tid_y_ampl_1hr',"Relative TID-Y amplitude at t=1hr",[0,0.002,0.01,0.02,0.05,0.1],more=float),  TDLOption('tid_y_size_km',"TID-Y size, in km",[25,50,100,200,1000],more=int),  TDLOption('tid_y_speed_kmh',"TID-Y speed, in km/h",[25,50,100,200,300,500],more=int),);wedge_options = TDLCompileMenu('Wedge model options',  TDLOption('wedge_min','Min delta-TEC over 100km',[0,0.001,0.1,1,2,5],more=float),  TDLOption('wedge_max','Max delta-TEC over 100km',[0,0.001,0.1,1,2,5],more=float),  TDLOption('wedge_time','Time to reach max delta-TEC, hours',[1,2,4,8],more=float));# Define a callback to be called whenever the iono_model option changes# This particular callback will show/hide the submenus based on the selected optioniono_model_option.when_changed(lambda model:\  (sine_options.show(model==sine_tid_model),  wedge_options.show(model==wedge_model)));```
This code exploits the following features: 

* The return value of TD![[LxxxOption|LxxxOption]]() is an "option" object. We have to retain the object if we want to manipulate the option later. Most of the time we just ignore it... 
* The `when_changed()` method of an option object registers a callback. Its argument must be a function taking a single argument. Whenever the value of the option is changed, the callback is invoked with the new value as its argument. (It is also invoked once initially, to set things up.) 
* The `show(bool)` method can be used to hide or show an option (or a whole option menu). An alternative method is `enable(bool)` -- this enables or disables (greys out) the option's menu item. Other methods may be implemented later as needed. 
* Callbacks can be anything --- here we construct a lambda function on the fly. A more verbose equivalent of the code above would be: ```python
def _show_model_options (model):  sine_options.show(model==sine_tid_model);  wedge_options.show(model==wedge_model);iono_model_option.when_changed(_show_model_options);```

# Available callbacks

The following callback methods are available: 


## when_changed()

This is illustrated in the example above. `option.when_changed(func)` takes one argument, which should be a callable taking a single argument. Whenever the value of the option is changed, the callback is invoked with the new value as its argument. It is also invoked once from `when_changed()` itself, to set things up. If the option is read from a tdl.conf file after the callback is registered, the callback is also invoked. 

Note that you can register multiple callbacks for a single option, by calling `when_changed()` repeatedly. 


## set_validator()

A validator callback can be registered to check if an option value is acceptable, as soon as it is entered by the user. This is only available for list-type options invoked with a `more=` argument, and for file/dir selectors (since for the other types of options, the possible values are known an restricted to begin with). 

Here's an example validator: 

* ```python
def _channel_validator (value):  return 0 <= value and value <= 20;opt = TDLRuntimeOption('channel_index',"Channel number",[0],more=int);opt.set_validator(_channel_validator);```
The validator function is called with a single argument, the new value to be tested. If the option was created with `more=int` or `more=float`, then the value passed in will already be an int or a float. If the option is a file or dir selector, then what is passed in is the full pathname. 

The validator should return `True` if the value is acceptable, or `False` (or throw an exception) if not. The GUI will not let the user enter values that fail the validator. 


# Other methods of option objects

To achieve true interactivity, your callbacks must be able to manipulate the state of option objects. The following is a list of available option methods. 


## Methods common to all option types


### value

`opt.value` is not really a method, but a data member. It contains the current value of the object. 


### show(show=True) & hide(hide=True)

`show()` and `hide()` show and hide the option in the GUI. They have a single boolean argument that defaults to True; setting it to False reverses the effect of the call. 


### enable(enable=True) & disable(disable=True)

`enable()` and `disable()` enable or disable (grey out) the option in the GUI. They have a single boolean argument that defaults to True; setting it to False reverses the effect of the call. 


### set_value(value,save=True)

`set_value()` changes the value of the option. At the moment minimal sanity checking is performed, specifically: 

* boolean options are forced to a boolean value. 
* for list-type options without a custom value (i.e. created without a `more=` keyword), the value must be present in the original opton list. For list-type options with a custom value, if the value is not in the list,  it will be set as the custom value. Note that the validator (if any) is not called in this case. 
* for file or directory options, no checking is done. 
If the optional `save` keyword is True, the new option setting will be saved to .tdl.conf. 


### set(n,save=True)

For list-type options, this selects option #n. For other options, this is equivalent to calling `set_value()`. 


### set_name(name)

Changes the name of the option (i.e. the text displayed in the menu). 


## Methods specific to list-type options


### set_custom_value(value,select=True,save=True)

This is only available for options with a custom value (i.e. created with a `more=` keyword). The function changes the custom value. If `select` is True, the custom entry is also selected. If `save` is True, the selection is saved to .tdl.conf. 


### set_option_list(list,select=None,conserve_selection=True)

This changes the list of available option settings. The `select` keyword can be set to an index in order to immediately select that option, otherwise it will attempt to preserve the current selection, unless `conserve_selection` is False. 


### num_options()

Returns the number of options in the list (+1 for the custom value, if supported) 


### get_option(n)

Returns the value of option #n in the list 


### get_option_str(n)

Returns the string representation (i.e. what is written to the config file) of option #n in the list. 


### get_option_desc(n)

Returns the string description (i.e. what is shown in the GUI) of option #n in the list. This is usually equivalent to `get_option_str()`, but for list options created from a dict the description may be different (e.g. more verbose.) 

   * 

## Methods specific to menus


### toggle options

Menus created with the `toggle` option exhibit all the behaviour (and support all the methods) of  boolean options. 


### set_summary(text)

Sets a summary text that is displayed to the right of the menu title. An initial value for the summary may be passed in via a `summary=` keyword when the menu is created. 
Clone this wiki locally