Interactivity between *Table View* Node and Plotly Plot in *Generic JavaScript View*

Hey everybody,

I have some troubles combining the interactivity of standard KNIME nodes and the Generic JavaScript View. I want to show a table (Table View node) and a box plot of values (Plotly box plot in Generic JavaScript view) on the same page and interaction between these two elements.

I would like to select rows in the table when data points in the plot are selected. It would be great to have the vice versa functionality to highlight data points in the plot if the corresponding row is selected, but this is additional.

Without any deeper knowledge in front end development I came quite far with the plot itself. But know I’m struggling to connect both features. Is there any documentation on that topic, can someone share a working example, or even better: adapt my workflow/code below?

Thanks in advance,
Best, J

Image of the plot:

Here is my dummy workflow for the problem:
Interactive Box Plot.knwf (14.7 KB)

Here is the code of the Generic JavaScript View:

// **************
// *  VARIABLES *
// **************

// Input data

// Read the input KNIME data table to kdt
var kdt = knimeDataTable;

// Read column "Measurement" to measurement_names
var measurement_names = kdt.getColumn(kdt.getColumnNames().indexOf("Measurement"));

// Read column "Old RMSE" to old_rmse_values
var old_rmse_values = kdt.getColumn(kdt.getColumnNames().indexOf("Old RMSE"));

// Read column "New RMSE" to new_rmse_values
var new_rmse_values = kdt.getColumn(kdt.getColumnNames().indexOf("New RMSE"));


// Layout parameters

// Set plot title
var plot_title = 'Box Plot of Root Mean Squared Relative Errors [ppm]';

// Set plot width
var plot_width = 800; // Set plot_export_width to integer if set to "auto"

// Set plot height
var plot_height = 500; // Set plot_export_height to integer if set to "auto"

// Set wisker width
var whisker_width = 0.2;


// Config parameters

// Set plot export filename
var plot_export_filename = 'calibration_rmse_boxplot';

// Set plot export format
var plot_export_format = 'svg'; // Available formats: png, svg, jpeg, webp

// Set plot export width
var plot_export_width = plot_width; // Set to integer if plot_export_width is "auto"

// Set plot export height
var plot_export_height = plot_height; // Set to integer if plot_export_height is "auto"

// Set the option to edit the plot
var plot_editable = false; // true or false


// ********
// * PLOT *
// ********

// Traces

// Trace 1 - Boxplot of new RMSE with jittered data
// points showing measurement names
var trace1 = {
	type: 'box',
	y: new_rmse_values,
	boxpoints: 'all',
	jitter: 0.5,
	pointpos: -2.0, // Value between -2.0 and 2.0
	name: 'New RMSE',
	hoverinfo: "all",
	hovertext: measurement_names,
	whiskerwidth: whisker_width,
	marker: {
		size: 10,
		opacity: .6
	}
};

// Trace 2 - Boxplot of old RMSE, which is by default
// not shown
var trace2 = {
	y: old_rmse_values,
	visible: "legendonly",
  	type: 'box',
	name: 'Old RMSE',
	boxpoints: 'Outliers', // Show only outliers as points
	hoverinfo: "all",
	hovertext: measurement_names,
	whiskerwidth: whisker_width
};

// Combine all three traces into data
var data = [trace1, trace2];


// Layout

// Set the general plot layout
var layout = {
	legend: true,
	title: plot_title,
	yaxis: {
		//rangemode: 'tozero',
		autorange: true
	},
	hovermode: 'closest', // Modes: 'closest', 'compare'
	dragmode: 'lasso',
	width: plot_width,
	height: plot_height
};

// Config

// Set the general plot configuration
var config = {
	toImageButtonOptions: {
		format: plot_export_format,
		filename: plot_export_filename,
		height: plot_export_height,
		width: plot_export_width,
		scale: 1, // Multiply title/legend/axis/canvas sizes by this factor
	},
	displaylogo: false,
	editable: plot_editable,
	modeBarButtons: [['toImage', 'lasso2d', 'zoom2d', 'zoomIn2d', 'zoomOut2d', 'resetScale2d']]
}

// ******** 
// * MAIN *
// ********

//Create the plotly HTML element 
var div = document.createElement('DIV');
div.setAttribute('id', 'rmse_boxplot');
document.body.append(div);

// Create the plot
Plotly.newPlot('rmse_boxplot', data, layout, config);

/*
//Knime selection event publishing; selection events
document.getElementById('rmse_boxplot').on('plotly_click', function (eventData) {
    if (!eventData) {
        return;
    }

    var selection = [];

    eventData.points.forEach(function (pt) {
        selection.push(pt.data.id[pt.pointIndex]);
    });

    knimeService.setSelectedRows(knimeDataTable.getTableId(), selection);
});

//Knime selection event publishing; de-selection events
document.getElementById('rmse_boxplot').on('plotly_deselect', function () {
    knimeService.setSelectedRows(knimeDataTable.getTableId(), []);
});

*/

document.getElementById('rmse_boxplot').on('plotly_selected', function(eventData) {
    var selectedPoints = eventData.points;

    // Get the trace indices of the selected points
    var selectedIndices = selectedPoints.map(function(point) {
        return point.curveNumber;
    });

    // Create an array to hold the opacity values for all points in trace1
    var opacityValues = new Array(new_rmse_values.length).fill(0.6); // Set the default opacity (0.6)

    // Set the opacity of the selected points to 1 (fully opaque)
    selectedIndices.forEach(function(index) {
        selectedPoints.forEach(function(point) {
            if (point.curveNumber === index) {
                opacityValues[point.pointIndex] = 1.0;
            }
        });
    });

    // Update the marker opacity of trace1 with the new opacity values
    Plotly.restyle('rmse_boxplot', {'marker.opacity': [opacityValues]}, [0]);
});

In case no one can help with this, is there any documentation about the interaction of JavaScript and KNIME?

Best, J

Hi @wurz and sorry for the long delay,

I would suggest using the Python View node since the publish and subscribe to events are handled by KNIME there. Here is a how-to blog post by @Mpattadkal:

Also, here is an example workflow:

If you have further questions, please feel free to ask. I know that @paolotamag would be happy to help.

1 Like

Hi there so if you still want to code interactivity in JavaScript there is a way but it requires strong coding skills.

Here is an example:

and the blog post that mentions the js functions to publish and subscribe interactive events:

2 Likes

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