Use ParameterForm
to easily create Orange widget forms#
Often, an Orange widget boils down to a form to specify inputs for its associated Ewoks task.
For this common use case, ewoksorange provides a ParameterForm
class. Once instantiated, it can generate graphical input elements based on the given input name and types.
For example, let’s take a simple Ewoks task SumList
and say we want to write an Orange widget that will allow the user to specify the inputs of this task.
from ewokscore.task import Task
class SumList(
Task,
input_names=["list"],
optional_input_names=["delay"],
output_names=["sum"],
):
"""Add items from a list"""
def run(self):
if self.inputs.list is None:
raise ValueError("list should be provided")
if self.inputs.delay:
delay = self.inputs.delay
else:
delay = 0
sum_ = 0
for i_elmt, elmt in enumerate(self.inputs.list):
sum_ += elmt
_sleep(delay)
self.outputs.sum = sum_
This task takes a list
as input and iterates over it to sum its elements with an optional delay
. We then want the Orange widget to provide us two GUI elements: one textbox to specify the list, and a numeric spinbox to specify the delay.
The Orange widget will then look like this:
import json
from ewoksorange.bindings import OWEwoksWidgetNoThread
from ewoksorange.gui.parameterform import ParameterForm
from ewokscore.tests.examples.tasks.sumlist import SumList
class OWSumList(
OWEwoksWidgetNoThread,
ewokstaskclass=SumList,
):
def __init__(self):
super().__init__()
self._parameter_form = ParameterForm(parent=self.controlArea)
self._parameter_form.addParameter(
"delay",
label="Delay for each sum iteration",
value_for_type=0,
value_change_callback=self._inputs_changed
)
self._parameter_form.addParameter(
"list",
label="List of elements to sum",
value_for_type="",
serialize=json.dumps,
deserialize=json.loads,
value_change_callback=self._inputs_changed
)
self._update_parameter_values()
def _inputs_changed(self):
new_values = self._parameter_form.get_parameter_values()
self.update_default_inputs(**new_values)
def _update_parameter_values(self):
initial_values = self.get_default_input_values()
self._parameter_form.set_parameter_values(initial_values)
Note
The full example can be found in src/orangecontrib/ewokstest/sumlist_parameter_form.py
There is a lot to unpack so let’s do this step by step:
self._parameter_form = ParameterForm(parent=self.controlArea)
This line creates the ParameterForm
in the controlArea
of the widget. The controlArea
(as opposed to the mainArea
) is the widget area where control elements (e.g. buttons) are located so it makes sense to put our form there.
self._parameter_form.addParameter(
"delay",
label="Delay for each sum iteration",
value_for_type=0,
value_change_callback=self._inputs_changed
)
This part calls addParameter
a first time to generate the first element of our form.
addParameter
only has one mandatory argument: the name (delay
) that is used to uniquely identify the parameter.
The other specified arguments are:
label
: A string that will be displayed next to the GUI element.value_for_type
: A Python value whose type will be used to determine the GUI element to show. In this case, we give anumber
(0
but it could be any number) so thatParameterForm
shows a numerical spinbox. See the end of the page for a list of all possible values.value_change_callback
: The function that will be called when the value is changed. We will come back to this later.
self._parameter_form.addParameter(
"list",
label="List of elements to sum",
value_for_type="",
serialize=json.dumps,
deserialize=json.loads,
value_change_callback=self._inputs_changed
)
This part calls addParameter
a second time to generate the second element of our form used to specify the list
parameter.
We already saw the label
and value_change_callback
arguments.
Since on the GUI side, a user can only input numbers or strings, this parameter will be a string
(hence the string
in value_for_type
so that the GUI will have a textbox).
However, we can apply a transformation to the value when retrieving it from the GUI: this is the role of the function given as deserialize
argument. By doing json.loads
on the string representing a list
, we can get a list as parameter value instead not a string. The serialize
is the inverse operation (when setting the value from the widget to the GUI).
def _inputs_changed(self):
new_values = self._parameter_form.get_parameter_values()
self.update_default_inputs(**new_values)
This is the function that will be called when the ParameterForm
values change. get_parameter_values
allows us to retrieve the dictionnary of parameter values (the keys being the name
specified in addParameter
).
For these values to be used as inputs of the Ewoks task, we must update the default inputs of the task. This is done by update_default_inputs
that takes arguments key=value
, the keys being the names of the Ewoks task inputs. Since the names of the parameters and of the Ewoks task inputs are the same (list
and delay
), we can directly use the new_values
dictionnary coming from the ParameterForm
.
Warning
The parameter values are set as default inputs. It means they will be overwritten by dynamic inputs that come from upstream connections or by execution inputs.
def _update_parameter_values(self):
initial_values = self.get_default_input_values()
self._parameter_form.set_parameter_values(initial_values)
A saved workflow can hold default values for each task. By calling this function when creating the Orange widget, we ensure that these initial default values are propagated to the parameter form so that it is initialized with the right values.
Possible value_for_type and their associated GUI elements#
Type |
value_for_type example |
GUI element |
Notes |
---|---|---|---|
bool |
True |
Checkbox |
|
Number |
0 |
Spinbox |
The spinbox will allow decimals only if a float is supplied |
Sequence |
[‘Choice1’, ‘Choice2’, ‘Choice3’] |
Combobox |
The choices of the combobox must be specified in the given sequence (an empty list will generate a combobox with no choices). |
str |
“” |
Textbox |
Can be used as input for list and dict if json is used for the serialization/deserialization. |
Note
In case the textbox is meant for a path (to a file/directory/HDF5 entity), the select argument can be supplied in conjunction of the string value_for_type.
Specifying the select argument will make a button appear next to the textbox that opens a file browser. Selecting an entity in this file browser will automatically fill the checkbox with the entity path.
The possible selectable entities depend on the value of the select argument: file, newfile, directory, h5dataset, h5group, files, newfiles, directories, h5datasets or h5groups.