Plugins - dialogos-project/dialogos GitHub Wiki
You can extend DialogOS with new node types by implementing plugins. This document describes how to do this.
Plugins
A plugin is a Java class that implements the Plugin interface. A plugin has a unique ID to distinguish it from other plugins; a human-readable name that is displayed in the DialogOS GUI; and may have an icon that is shown in the GUI as well.
Its main function is to register a bunch of node types with DialogOS, using the method registerNodeTypes. These node types are displayed in the main node palette in the DialogOS window, and the user can create new nodes of these types by dragging them onto the graph editor.
Nodes
Nodes are Java classes that are derived from the base class Node. They must implement two abstract methods: execute and createEditorComponent.
Execute
The execute method is called whenever an instance of the node type becomes "active" while DialogOS executes the dialog graph. This is the method that performs whatever work nodes of this type are supposed to perform.
The execute method is expected to return the node in the dialog graph that should be executed next. The method can obtain the target node to which the k-th outgoing port of the node is connected by calling getEdge(k).getTarget()
(0-indexed).
While execute is running, it is typically useful to access the properties that the user set in the node's Properties window (which is displayed when the user either double-clicks the node or right-clicks and then selects "Properties..."). These properties are set in the node's constructor and when the user closes the Properties window by clicking "Ok". Their values can be accessed through the getProperty method.
Creating editor components
Furthermore, the Node class must implement the method createEditorComponent, which is called whenever the user opens the node's Properties window. The method is expected to return an object of class JTabbedPane. Inside of the tabbed pane, you can put any Swing components you like. Notice that your code will usually not open its own windows (i.e. JFrames or JDialogs). It will simply return a JTabbedPane that probably contains a JPanel as its top-level content. You will then place JLabels and components for editing properties into the JPanel.
DialogOS simplifies this last step by providing methods which will automatically create Swing components for editing properties in the NodePropertiesDialog class. If a property is rendered as a component provided by this class, DialogOS will also automatically update the value of the property when the user closes the Properties window by clicking the "Ok" button.
Properties can be automatically saved to and loaded from the dialog file. If you want this, overwrite the methods Node#readAttribute and Node#writeAttributes so they read and write the attributes from XMLReader
and XMLWriter
arguments, respectively.
Constructor
The constructor of a Node subclass will typically add ports for outgoing edges to your node. It does this by calling addEdge as many times as you want outgoing ports. You can also modify the outgoing ports later if your node requires it.
The constructor will probably also create properties by calling setProperty(PROPERTY_NAME, INITIAL_VALUE)
; replace "PROPERTY_NAME" by the name you would like to give your property and "INITIAL_VALUE" by its initial value. Properties can be of any data type you like (e.g. strings, ints, booleans).
Node names and icons
The name of a node that is shown in the node palette of DialogOS is determined in a complex way. The default name of the node is the class name (e.g. OutputNode
); if the class name ends in "...Node", this suffix is removed (i.e., the name becomes Output
). DialogOS then localizes this name through its localization resource mechanism to obtain the node name that is shown in the GUI.
In practice, it can be easier to overwrite the static method getNodeTypeName in a subclass of Node with a public
implementation. This will directly set the name for the node type.
In addition to a human-readable name, a node type may also have an icon by putting PNG files (16x16 pixels) into the classpath. For the node class MyNode
in the package de.dialogos.plugin
, DialogOS will look for the icon in a file called MyNode.png
in the directory de/dialogos/plugin
in the classpath. In practice, one usually places the PNG file into the directory src/main/resources/de/dialogos/plugin
; the build tool (Gradle, Maven, etc.) will then package it into the Jar file under the correct path.
If such a file cannot be found, DialogOS will simply not display an icon for the node type.
Declaring a plugin to DialogOS
To make a plugin visible to DialogOS, you need to supply a file META-INF/services/com.clt.dialogos.plugin.Plugin
. This file needs to contain the fully qualified name of your plugin class. So if your plugin class is com.example.Plugin
, the file should look as follows:
com.example.MyPlugin
The easiest way to bundle such a file with your plugin is to put it in src/main/resources/META-INF/services/com.clt.dialogos.plugin.Plugin
and use your build tool (e.g. Gradle or Maven) to create a Jar file with all your classes and resources. This should put the file in the right place inside the Jar file.
Global settings
You can create a GUI for editing global settings for the plugin as a whole. This GUI will be displayed under the menu entry for your plugin in the Dialog menu of DialogOS.
Your Plugin class has a method createDefaultSettings
, which returns an instance of a subclass of PluginSettings. You can create your own subclass of PluginSettings and return the GUI for the global settings (as a Swing component) in its createEditor
method.
Just like for the individual nodes, you can define the individual global settings as properties to have them automatically updated when you edit them in the GUI, and you can have your properties automatically saved to a file with the graph by overwriting the methods readAttribute
and writeAttributes
of PluginSettings
.
Evaluating expressions
By default, the getProperty method will return the string the user entered in the GUI of your node literally. You may also be interested in interpreting this string as a DialogOS expression and evaluating the expression with respect to the current values of the variables.
To do this, you need to first convert the string you got from getProperty into an Expression using the parseExpression method of the Node class. You can then evaluate the Expression into a Value using its evaluate method. Here is some example code from the SQLite plugin (where this
is an instance of a subclass of Node):
String expressionString = this.getProperty(QUERY).toString();
Expression expr = this.parseExpression(expressionString);
Value v = expr.evaluate();
String query = ((StringValue) v).getString();
Running a plugin during development
While you develop your plugin, the easiest way to test it is to set
mainClassName = 'com.clt.dialogos.DialogOS'
in the build.gradle
file of the plugin project. Then you can run ./gradlew run
to start DialogOS with your plugin loaded. Notice that this run of DialogOS will contain only your own plugin and the standard plugins (speech recognizer & synthesis), and you will only have access to the English speech recognizer and synthesizer.
Deploying your plugin
If you run DialogOS in some other way, your plugin and all of its dependencies need to be on the classpath.
You will therefore use gradlew build
to create a Jar file of your plugin. If you want, you can use the Shadow Jar plugin for Gradle to create a single Jar file that contains both your own classes and the classes from all the Jars on which your plugin depends.
To deploy a plugin into an existing DialogOS installation (from an official DialogOS release), proceed as follows.
- Find the directory into which the installer installed DialogOS. On MacOS, this is
/Applications/DialogOS
. - Within this directory, find the
plugins
subdirectory. - Copy the Jar file for your plugin into the
plugins
directory. If you did not include all dependencies using the Shadow Jar plugin, you also need to copy the Jar files for all dependencies (except DialogOS itself) there.
When you next start DialogOS, you should see that your plugin is being loaded on the startup screen, and the nodes from your plugin should be available.
Examples
See the plugin dialogos-plugin-minimalio
for a minimal plugin implementation.
The dialogos-project organization on Github contains the source code for a number of more complex plugins, such as the SQLite and the NXT plugin.
If you would like to implement a DialogOS plugin in Python, see https://github.com/dialogos-project/jython-plugin .