KNIME Python Extension View + Output Port

This probably falls within the category of “not supported yet”, but I am trying to build a Python Extension View that both shows the visualisation and also has an output port to pass the visual as a binary object to a table.

I am having difficulty defining the schema returned by configure as there appears to be no way to create a schema with a view as the first entry and a table as the second entry. I suspect that I will also have difficulty at the execute stage as well.

Code below. It’s a long shot but any help would be appreciated. Note: I’ve reached into the knime.api.views package to pull out the NodeView class so that I can use it in type annotation.

import knime.extension as knext
import knime.api.views as knviews

@knext.node(
    name="Plotly Bar",
    node_type=knext.NodeType.VISUALIZER,
    icon_path="icon.png",
    category="/",
)
@knext.input_table(name="Input Data", description="Input data.")
@knext.output_view(name="Plotly Bar", description="Bar chart.")
@knext.output_table(name="Output_image", description="Image in a table.")
class TemplateNode:
    """Visualise a plotly bar chart."""

    x_column = knext.ColumnParameter(
        label="X-axis column",
       description="Values to use for the x-axis", port_index=0
    )
    y_columns = knext.MultiColumnParameter(
        label="Y-axis columns",
        description="Values to use for the y-axis", port_index=0
    )

    def configure(self, configure_context, *input_schemas) -> knext.Schema:
        column_types: List[knext.KnimeType] = [type(knviews.NodeView), knext.blob()]
        column_names: List[str] = ["view, " "image"]
        output_schema = knext.Schema(
            ktypes=column_types,
            names=column_names,
        )
        return output_schema

    def execute(
        self, exec_context: knext.ConfigurationContext, *input_ports
    ) -> List[Union[knviews.NodeView, knext.Table]]:

        df = input_ports[0].to_pandas()
        fig = px.bar(df, x=self.x_column, y=self.y_columns)

        df_output = pd.DataFrame({"image": [fig.to_image(format="png")]})
        output_table = knext.Table.from_pandas(df_output)

        return [knext.view_plotly(fig), output_table]

DiaAzul
LinkedIn | Medium | GitHub

Hi @DiaAzul,

Thank you for the question. This is definitely missing from our documentation. Actually, it’s quite easy:

# [...]
# It does not matter if the output_view decorator is used before or after the output_table decorator
@knext.output_view(name="Plotly Bar", description="Bar chart.")
@knext.output_table(name="Output_image", description="Image in a table.")
class TemplateNode:

    # [...]

    def configure(self, configure_context, *input_schemas) -> knext.Schema:
        # [...]

        # You only need to return the schema(s) of the table(s)
        return output_schema

    def execute(
        self, exec_context: knext.ConfigurationContext, *input_ports
    ) -> List[Union[knviews.NodeView, knext.Table]]:
        # [...]

        # The output view must be the last element of the return value
        return output_table, output_view

        # For multiple table outputs use
        # return output_table_1, output_table_2, output_view

I will make sure that our docs are updated.

3 Likes

@bwilhelm

Thanks for the example. That works (as far as configuring the nodes), but there is no binary blob interface between Python and JAVA :frowning: so I’m blocked until we can return an image from the View node to the JAVA workflow.

Also, probably too late to change now (though probably better now than never), the NodeView returned from the execute function is a mandatory parameter and should be first, not last, in the list. This will cause a lot of confusion as it does not follow programming convention.

Another, very minor comment. Your type hinting in the tooltip shows a vertical pipe to represent a union operator in the definition of the return value from execute(). This doesn’t appear in the Python spec until version 3.10; however, the KNIME extension development environment in the yml file specifies 3.9. I know that I am pedantic, but can you keep consistency between what you require for development and what you document. Details matter, and KNIME is very bad at details.

Can you also nudge the development team to expose the NodeView class in the init file that is used to expose items in the package. You have NodeView in the type hints for the documentation, but I cannot add it as a type hint in the code without accessing the class directly (better to expose the class as part of the package). This is not really sustainable, if you change the file/folder structure then I need to re-write all my code.

Thanks

DiaAzul
LinkedIn | Medium | GitHub

1 Like

Sorry, additional comment

You might want to check your type hint as it always requires None, NodeView if there are no output tables (note, this is the hint for type checking not the Python code requirement). You may want to consider the following which will allow a return type of NodeView on its own or a list of output tables with a NodeView at the end.

Union[knviews.NodeView, Tuple[Tuple[knext.Table, ...], knviews.NodeView]]

DiaAzul

This topic was automatically closed 7 days after the last reply. New replies are no longer allowed.