ConfigurableNodeDialog example

Is there anywhere where there is an example implementation of ConfigurableNodeDialog which I can take a look at?

Thanks

Steve

1 Like

Hi Steve,
Take a look at WorkflowExecutorNodeDialogPane in the org.knime.buildworkflows plugin.
That code is not yet on Github, you can get it from the plugin as described in:

I added it to this gist for you:

best,
Gabriel

1 Like

Thanks Gabriel - that’s very useful.

Couple of follow-up questions…

  1. I assume I am meant to modify and return the incoming ModifiableNodeCreationConfiguration rather than completely rebuild it?
  2. In my ‘ideal’ scenario, I would have a 1 → n node, where n >= 2, and I can change the port type of the input to any port type, and then each output to either the input port type or flow variable (but nothing else!) Is that possible with the current state of the API, or is that a step too far?

(And I hadn’t really come across those ‘workflow’ nodes and ports before - they are seriously mindbending!)

Thanks

Steve

1 Like
  1. Yes, that is the way it is used in that node, which is actually the only example in our codebase for that kind of dialog.

  2. I don’t see why that should not be possible. The only difficulty could be the dialog for choosing the port type. You might want to take a look at the way this is done in the component configuration.

Indeed, I am excited to see what people will do with them once they realize their full potential.

best,
Gabriel

Thanks Gabriel.

The big difficulty for 2 is that PortsConfigurationBuilder doesn’t allow an ‘exchangeable extendable’ grouping - they are either exchangeable or extendable. I did ponder what would happen if I manually constructed a ModifiablePortsConfiguration with a ConfigurablePortGroup implementation implementing both ExchangeablePortGroup and ExtendablePortGroup, and then decided not too - mainly because I wasn’t sure what would happen and it seemed like a lot of work to try to find out!

This nearly works in the node’s contextual menu:

PortsConfigurationBuilder b = new PortsConfigurationBuilder();

		// We add exactly one input - which is exchangeable
		// If this is a FlowVariable, it will be made optional, otherwise it is
		// the type used for all outputs which are not defined 'variable'
		b.addExchangeableInputPortGroup("Input", BufferedDataTable.TYPE,
				portTypes);

		// Now we add an exchangeable output port which forms the required first
		// output port
		// This can be either Flow Variable or 'Data', which means actually
		// whatever the input is (handled in the NodeModel)
		b.addExchangeableOutputPortGroup("Output", BufferedDataTable.TYPE,
				BufferedDataTable.TYPE, FlowVariablePortObject.TYPE);

		// Now we add an extendable group of ports, each of which is either
		// 'Data' or Flow Variable, as above - we cant change directly from the node 
		// menu, only by removing / re-adding
		b.addExtendableOutputPortGroup("More Outputs", new PortType[0],
				BufferedDataTable.TYPE, FlowVariablePortObject.TYPE);

		// Finally an exchangeable output port for the last output - types again as
		// above
		b.addExchangeableOutputPortGroup("Last' Output",
				BufferedDataTable.TYPE, BufferedDataTable.TYPE,
				FlowVariablePortObject.TYPE);
		return Optional.of(b);

The result of the above is

imageimage
(For a randomly selected input port type!)

In the node dialog, I think I can use a JComboBox for the port type of the input port and those outputs marked ‘Data’, and a checkbox in the settings for each output to flag it as Flow Variable instead, and rework the contents of the ModifiablePortsConfiguration from there.

More anon…

Steve

1 Like

OK, and here is the follow-up.

PortType can be changed from a JComboBox in the dialog. I Actually used DialogComponentStringSelection for the purpose (more on the slight concern I have about that later!).

Various snippets of code combined here to show the working parts. PortType doesn’t expose an Icon, so for those of us helped by the visual triggers of such things, the code below produces a reasonable approximation…

// List the PortTypes we want the user to see
PortType[] portTypes =
	PortTypeRegistry.getInstance().availablePortTypes().stream()
			.filter(pt -> !pt.isHidden() && !pt.isOptional())
			.toArray(PortType[]::new);

// Wrap them with an Icon for the dropdown
DefaultStringIconOption[] pTypes = Arrays.stream(portTypes).map(
		pt -> new DefaultStringIconOption(pt.getName(), new Icon() {

			@Override
			public void paintIcon(Component c, Graphics g, int x,
					int y) {
				g.setColor(new Color(pt.getColor()));
				if (pt.equals(BufferedDataTable.TYPE)) {
					g.fillPolygon(new int[] { 3, 10, 3 },
							new int[] { 3, 7, 11 }, 3);
				} else if (pt.equals(FlowVariablePortObject.TYPE)) {
					Graphics2D g2D = (Graphics2D) g;
					Ellipse2D circle = new Ellipse2D.Double(3, 3, 8, 8);
					g2D.fill(circle);
				} else {
					g.fillRect(3, 3, 8, 8);
				}
			}

			@Override
			public int getIconWidth() {
				return 12;
			}

			@Override
			public int getIconHeight() {
				return 12;
			}
		})).toArray(DefaultStringIconOption[]::new);

		portTypeMdl = createPortTypeModel();
		addDialogComponent(new DialogComponentStringSelection(portTypeMdl,
				PORT_TYPE, pTypes));

This looks like the following:

image

Flipping outputs between the selected port type and flow variable is then via JCheckBox (or DialogComponentBoolean). Updating those components / models from #loadAdditionalSettings in the dialog, and then if they have been changed rebuilding the ModifiableNodeCreationConfiguration from their content.

My slight concern is what might happen if someone (and I can imagine me being that ‘someone’!) was to try to override those settings models with a flow variable… I cannot actually imagine a scenario whereby the make-up of a workflow would be drastically changed in that way by the value of a flow variable, but this DialogComponent / SettingsModel-based approach does leave that as a possibility - the first instance of self-modifying code in KNIME?! Given that the rest of my node dialog is based on a custom DialogComponent and corresponding SettingsModel implementation, it is probably easier / safer to create the dialog in NodeDialogPane rather than DefaultNodeSettingsPane (That would also almost certainly give more flexibility for adding / removing ports and their corresponding options pane in the dialog too)

Steve

2 Likes