Starting my first ewoks orange widget#

In this chapter we will:

  • see how to do a basic connection between an ewoks task and an orangecontrib widget

Setting up ewoksorange#

Please make sure you already have ewoksorange install on your python environment. Else please see installation

Defining an ewoks task#

For this tutorial we would like to add a dedicated interface for the a task performing some data clipping from percentiles. We already have implemented it with ewokscore.

"""
clipdata.py: Core code for the ClipDataTask, which is a task to rescale 'data' (numpy array) to the given percentiles.
Includes the ewoks task and the pydantic models.
"""

import numpy
from ewokscore.model import BaseInputModel
from ewokscore.model import BaseOutputModel
from ewokscore.task import Task
from pydantic import Field


class InputModel(BaseInputModel):
    data: numpy.ndarray = Field(..., description="data to rescale")
    percentiles: tuple[float, float] = Field(
        ...,
        description="""percentiles to use for rescaling, must be a tuple of two values (p_min, p_max) with p_min <= p_max""",
    )


class OutputModel(BaseOutputModel):
    data: numpy.ndarray = Field(..., description="rescaled data")


class ClipDataTask(
    Task,
    input_model=InputModel,
    output_model=OutputModel,
):
    """
    Task to rescale 'data' (numpy array) to the given percentiles.
    """

    def run(self):
        data = self.inputs.data
        # compute data min and max
        percentiles = self.inputs.percentiles

        self.outputs.data = numpy.clip(
            data,
            a_min=numpy.percentile(data, percentiles[0]),
            a_max=numpy.percentile(data, percentiles[1]),
        )

Note

While input_model and output_model are optional, using them ensures type consistency and improves integration with Orange (allows link type data checking for example). For this tutorial, assume they are defined.

Hint

For simplicity, this tutorial keeps models in the same file as the task. For complex projects please consider defining them in a dedicated file (e.g., my_project.models.clip_data) and import them.

Associate a task to a dedicated orange widget#

Now we want to create a widget associated to the task.

There is different ways to define the ewoks tasks execution with orange (see Ewoks widgets and execution).

On this example we will take the ewoks widget doing the processing in a single thread (Execute the associated Ewoks task in a single dedicated thread). Because this will make sure the gui will not freeze with it and we don’t need concurrent execution.

Widget ‘skeleton’ is the following:

 1from ewoksorange.gui.owwidgets.threaded import OWEwoksWidgetOneThread
 2from [my_project].tasks.clipdata import ClipDataTask
 3
 4class OWClipData(
 5    OWEwoksWidgetOneThread,
 6    ewokstaskclass=ClipDataTask,
 7):
 8    name = "rescale data"
 9    id = "orange.widgets.my_project.ClipDataTask"
10    description = (
11        "widget to clip data (numpy array) within a percentile range."
12    )
13    pass

Hint

  • l1: import of the required base class for the widget.

  • l2: import of the task to bind with the widget.

  • l4: OW stand for Orange Widget.

  • l5: inheritance with the ewoks orange widget.

  • l6: definition of the ewoks task to bind. This is usually given with the full module path. For example if RescaleDataTask is saved in my_project.tasks.rescale the value would be my_project.tasks.rescale.RescaleDataTask.

  • l8: the name of the widget (will be displayed in the canvas).

  • l9: id from the orange point of view. It should be constant with time to ensure workflow compatibility.

  • l10: tooltip of the widget.

Tip

If Orange is installed, you can preview the widget by running

from Orange.widgets.utils.widgetpreview import WidgetPreview

WidgetPreview(OWClipData).run()

Further reading#

How to test this first widget ?