Is there anywhere where there is an example implementation of ConfigurableNodeDialog
which I can take a look at?
Thanks
Steve
Is there anywhere where there is an example implementation of ConfigurableNodeDialog
which I can take a look at?
Thanks
Steve
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
Thanks Gabriel - that’s very useful.
Couple of follow-up questions…
ModifiableNodeCreationConfiguration
rather than completely rebuild it?(And I hadn’t really come across those ‘workflow’ nodes and ports before - they are seriously mindbending!)
Thanks
Steve
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.
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
→
(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
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:
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