Skip to main content

Set constraints on input fields

Changed in v12.1.0

ToggleButton has been renamed to BooleanField.

The parametrization constraints can be used for two purposes:

  • Dynamic setting of the minimum and/or maximum boundary of a number field
  • Dynamic setting of the visibility of an input field

There are four methods of implementing constraints, which are listed in the table below in order of complexity:

methodmin/maxvisibility
staticmin=1visible=True
Lookupmin=Lookup('field_x')visible=Lookup('field_x')
RowLookup (DynamicArray)min=RowLookup('field_x')visible=RowLookup('field_x')
BoolOperatorN/Avisible=And(Lookup('field_x'), Lookup('field_y'))
callback functionmin=get_minvisible=get_visibility

A more elaborate example of these methods is given below.

Static

In the case of a static boundary constraint (e.g. field should always be greater than zero), the numerical value is simply inserted in the optional min or max argument of the Field.

param_x = NumberField('X', min=0)

A field is by default visible, but can be made "hidden" in the interface by directly setting a boolean on the visible argument:

param_x = NumberField('X', visible=False)

Lookup

Sometimes the constraint should be dynamic, such that the evaluation of the constraint depends on the input of one or more other input fields. For example, param_y should always be larger than param_x. This is achieved using a Lookup (i.e. looks up the value of the defined field when the constraint is evaluated):

param_x = NumberField('X')
param_y = NumberField('Y', min=Lookup('param_x'))

In case of a nested parametrization structure, the dotted path should be used:

Lookup('tab.section.param_x')

In a similar way the visibility can be dynamic. Keep in mind that the Lookup in this case should evaluate to a boolean, and is therefore in practice often used on a BooleanField:

param_x = BooleanField('X')
param_y = NumberField('Y', visible=Lookup('param_x'))

When the Lookup cannot find the target field, a warning is raised and the visibility or min/max boundary is set to its default (i.e. visible=True, min=None, max=None).

RowLookup

Similar to the Lookup, a RowLookup may be used to set the visibility or boundaries on the min/max of a field in a DynamicArray. In this way, a field of a specific row within the array can be made dependent of another field within the same row.

array = DynamicArray('Array')
array.param_x = NumberField('X')
array.param_y = NumberField('Y', min=RowLookup('param_x'))

BoolOperator

VIKTOR offers a few specific boolean operators, to make it easy to specify the visibility. These operators evaluate to a boolean and can therefore not be used on the min/max of a field.

The following BoolOperators can be used:

BoolOperatorCan be used to
Andevaluate multiple operands to be True
Orevaluate if at least one operand is True
Notevaluate an operand to be False
IsEqualevaluate two operands to be equal
IsNotEqualevaluate two operands to be NOT equal
IsTrueevaluate an operand to be True
IsFalseevaluate an operand to be False
IsNotNoneevaluate an operand to be NOT None

For example, param_z should become visible only when the following conditions are both met:

  • param_x is equal to False
  • param_y is not equal to 5
_param_z_visible = And(    IsFalse(Lookup('param_x')),    IsEqual(Lookup('param_y'), 5))param_x = BooleanField('X')param_y = NumberField('Y')param_z = NumberField('Z', visible=_param_z_visible)

These boolean operators do not assert truthiness of non-boolean types (e.g. Python may treat an empty string as False, but this will be ignored). Inconsistent types in a boolean operator will raise a warning and if it is used to set the visibility of a field, this will be set to True by default. In a similar way, non-numeric evaluations used to set the min/max boundary of an input field will raise a warning and set the boundary to None by default.

Callback function

If above boolean operators cannot suffice your needs, specific functions can be created which can contain more complex logic if necessary. For the purpose of this example, we will implement the same constraint as the previous example. A callback function works similar to a callback function on the options of an OptionField. The SDK detects when a custom function is passed, and will evaluate it with the params, entity_id, and entity_name as input. The example of the boolean operators can be written as a callback function like this:

def param_z_visible(params, **kwargs):    return params.param_x is False and (params.param_y == 5)class Parametrization(ViktorParametrization):    param_x = BooleanField('X')    param_y = NumberField('Y')    param_z = NumberField('Z', visible=param_z_visible)
tip

All individual kwargs can be added explicitly in the signature if needed:

def param_z_visible(params, entity_id, entity_name, **kwargs):
...

For constraints that have to be applied to fields within a DynamicArray, make sure that the callback function returns a list of the constraint values, one for each row in the array. Therefore, it is important that the length of this list is equal to the number of rows in an array, otherwise (a warning is logged and) the constraint is ignored:

def array_param_z_visible(params, **kwargs):    return [row.param_x is False and (row.param_y == 5) for row in params.array]class Parametrization(ViktorParametrization):    array = DynamicArray('Array')    array.param_x = BooleanField('X')    array.param_y = NumberField('Y')    array.param_z = NumberField('Z', visible=array_param_z_visible)

When the visibility or min/max depends on data of another entity, the entity_id can be used.

Truthiness

With Python, it is possible to assert data of different types, for example:

param_x = True  # booleanparam_y = 1  # integerparam_z = 'false'  # stringif param_x and param_y and param_z:    print('Statement is true')  # -> this will be printedelse:    print('Statement is false')

However, this may lead to unexpected behavior in some use cases. In the case of VIKTOR constraints, truthiness of conflicting types as shown above is not taken into account. Suppose we want to use above listed fields in an IsEqual operator:

IsEqual(Lookup('param_x'), Lookup('param_y'), Lookup('param_z'))

this will always result in False, no matter what the inputs of these fields are. If you explicitly like to assert on truthiness, a custom FunctionLookup may be considered:

def assert_on_truthiness(param_x, param_y, param_z):    return param_x and param_y and param_zFunctionLookup(assert_on_truthiness, Lookup('param_x'), Lookup('param_y'), Lookup('param_z'))