Skip to content

Plugable Dialogs

johnmay edited this page Jan 20, 2013 · 28 revisions

Extension Points

Metingear can be extended easily with 'plugable dialogs'. The scope falls short of a full plugin framework which may be implemented in future but allows quick and easy extension to perform algorithms, edit and create entities by simply adding classes to the java classpath.

Pattern Rename

To start with we will create a simple dialog that will rename entities in our model with a regular expression.

Creating the dialog

Metingear exposes the interface ControlDialog which provides most of tools you will need to 'Control' the UI (User Interface). Although you could specify the entire interface yourself there would be a lot of overhead in handling required some required methods and it is easier to use the abstract class AbstractControlDialog available in the metingear-extension module.

public class PatternRename extends AbstractControlDialog {

    // constructor needed for instantiation
    public PatternRename(Window window){
        super(window);
    }

}

The next task is to create the Swing UI components for the dialog. The control dialog has a set* layout of three sections; information, form and navigation. The information provides a brief description of the dialog function, form provides a location for variable input and the navigation provides run/close buttons. In most case you will only need to override the creation of information and form sections. Overriding of the navigation is only needed if you have a more complex control structure (e.g. in a wizard a single dialog show multiple forms). For our pattern rename dialog we will need two text fields for the find/replace strings for this we will use JTextField from Swing. Although you can use the default JTextField I recommend using the factories provided by CAF which will make your fields/labels look the same as the rest of the application.

public class PatternRename extends AbstractControlDialog {

    private JTextField pattern = FieldFactory.newField(15);
    private JTextField replace = FieldFactory.newField(15);

    public PatternRename(Window window) {
        super(window);
    }

    @Override
    public JComponent createForm() {

        Box box = Box.createVerticalBox();

        box.add(pattern);
        box.add(replace);

        return box;

    }

}

The above code creates a dialog that looks as so:

box-dialog

To improve the look of the dialog we can add some labels to the input. Form layout can get very complex, to help improve the layout you can use the JGoodies Forms library. The PanelFactory also has a convenience method to set the JGoodies specification during instantiation

public class PatternRename extends AbstractControlDialog {

    private JTextField pattern = FieldFactory.newField(15);
    private JTextField replace = FieldFactory.newField(15);

    public PatternRename(Window window) {
        super(window);
    }

    @Override
    public JLabel createInformation() {
        return LabelFactory.newLabel("Rename metabolites using a pattern and a replacement");
    }

    @Override
    public JComponent createForm() {

        JPanel panel = PanelFactory.createDialogPanel("p, 4dlu, p:grow",
                                                      "p, 4dlu, p");

        CellConstraints cc = new CellConstraints();

        panel.add(LabelFactory.newFormLabel("Pattern:"), cc.xy(1, 1));
        panel.add(pattern,                               cc.xy(3, 1));

        panel.add(LabelFactory.newFormLabel("Replace:"), cc.xy(1, 3));
        panel.add(replace,                               cc.xy(3, 3));

        return panel;

    }

}

New dialog look:

jgoodies-dialog

Now we have our dialog specified we need add the processing. This is done be overriding the processing method with your desired function.

    @Override
    public void process() {

        // get the field values
        Pattern pattern     = Pattern.compile(this.pattern.getText());
        String  replacement = replace.getText();

        // iterate over selected metabolites and set the name 
        // replacement if a match is found
        for(Metabolite metabolite : getSelection(Metabolite.class)){
            
            String  name    = metabolite.getName();
            Matcher matcher = pattern.matcher(name);
            
            if(matcher.find()){
                metabolite.setName(matcher.replaceAll(replacement));
            }
            
        }

    }

We now need to add additional information about the dialog such as where it will be in the menu, the name of the action as well as the context needed (when it is active). This is achieved via the PlugableDialog interface. The plugable dialog provides three simple methods:

public interface PlugableDialog {

    public List<String> getMenuPath();

    public Class<? extends ControlDialog> getDialogClass();

    public ContextResponder getContext();

}
  • getMenuPath() - provides the path on the menu system where the dialog will be added. New menus are automatically created for the dialog if needed.
  • getDialogClass() - provides the class of the dialog (the dialog is instantiated using reflection)
  • getContext() - the context needed for the dialog (e.g. when it is active)
public class PatternRenamePlugin implements PlugableDialog {

    @Override
    public List<String> getMenuPath() {
        // dialog will be on menu Tools>Plugins>... 
        return Arrays.asList("Tools", "Plugins");
    }

    @Override
    public Class<? extends ControlDialog> getDialogClass() {
        // our dialog class
        return PatternRename.class; 
    }

    @Override
    public ContextResponder getContext() {
        return new ContextResponder() {
            @Override
            public boolean getContext(ReconstructionManager reconstructionManager,
                                      Reconstruction         reconstruction,
                                      EntityCollection       selection) {
                // only available when one or more metabolites are selected
                return selection.hasSelection(Metabolite.class);
            }
        };
    }
}

The final step is create the configuration files. The service provider interface requires implementing classes to list themselves in the META-INF/services/* of the java archive .jar. We therefore need to create a file uk.ac.ebi.metingear.view.PlugableDialog and list our new dialog <your.package>.PatternRename. We also need more information about the menu item. This is provided by an action.properties file in the same package as the dialog:

PatternRename.Action.Name=Pattern Rename
PatternRename.Action.ShortDescription=Rename metabolites using a given pattern

To run Metingear with the plugin simply include the jar in the classpath (with any additionally required classpaths):

java -Xmx1G -Xms500M -cp Metingear-0.9-SNAPSHOT.jar:sample-extension-1.0.jar uk.ac.ebi.metingear.Main

*Although you can create custom looking dialogs this is outside the scope of this tutorial.

Clone this wiki locally