Introduction

It is impossible for one piece of bioinformatics software to suit every type of experimental data, every type of analysis or every type of visualization. That's why the plugin interface of PathVisio is very important: it let's you customize PathVisio to your needs.

What can a plugin do?

What type of things can be a plugin? Here is a list of ideas. You can create plugins to:

  • Import or export to a new pathway file format
  • Import experimental data from a webservice and display it on pathways
  • Use a new style of data visualization, for example graphs instead of the simple colored boxes.
  • Implement a new statistical method for comparing expression on pathway
  • And many more...

Theoretically, every aspect of PathVisio can be customized. The menu's, the main window, the visualization engine and the expression data manager. There is no limit to what plugins can do.

In practice, there may be things that are not convenient to change, i.e. it would take a lot of work to get the type of customization you want. In those cases, please contact us to discuss what you want to achieve. You may have stumbled on a new type of usage that we haven't thought of, and we would love to hear from you. We may be able to modify the core of PathVisio to make your work easier.

Running Plugins

Note Webstart security model prevents plugins from local disk to be added to them. If you want to run a plugin, either run PathVisio from your IDE, or install PathVisio using one of the methods described in AlternativeInstallation

You can specify the location of your plugin with the -p command-line option. PathVisio expects one of three things after the -p option:

  1. A jar file, for example -p /home/martijn/helloworld.jar
  2. A directory containing multiple jar files, for example -p /home/martijn/cool/plugin/directory
  3. A fully specified class name of a class that implements org.pathvisio.Plugin, for example -p org.pathvisio.plugin.HelloWorld. For this to work the specified class has to be on the Class-Path. This is only interesting for developers because end-users have no control over the Class-Path. But for developing / testing it can be really useful.

Finally, there is a default plugin directory where you can place your plugin, in which case it will be automatically detected and loaded without having to specify the location.

Setting up your development environment

At this time, we highly recommend always using the latest subversion trunk of PathVisio. PathVisio is still in development, and relying on a release tarball may let your plugin get out of date quicker than you'd think.

To set up your build environment in Eclipse, see: PluginEclipseSetup?

Now create a separate Eclipse project and write your Plugin class there. Make sure you set the pathvisio project as a dependency of the plugin project, so that all exposed classes of PathVisio are available in your plugin and it compiles correctly. Specify the -p <your.PluginClass?> as a program argument in the run configuration.

To troubleshoot, do make sure that:

  1. Both pathvisio and your plugin compile without errors.
  2. Because your plugin project normally depends on the pathvisio project, all classes that are in pathvisio are also available in the plugin but not the other way around. Hence, the run configuration should use the plugin as project.
  3. specify the regular main class of PathVisio as the class to run: org.pathvisio.gui.swing.GuiMain?

A note on API stability

As mentioned, PathVisio is not completely stable. The API changes occasionally, and all plugins have to be updated. At the moment, we rely on automated continuous integration testing to make sure that all plugins conform to the API at all times. If an API leads to a broken plugin, we will notify and try to fix it automatically.

See this blog entry for an explanation of the  PathVisio stable API policy

HOWTO package your plugin as a jar

For quicker plugin loading, specify the class using the PathVisio-Plugin-Class Manifest attribute.

For example, add these lines to your build.xml:

  <target name="jar" depends="build">
    <jar jarfile="${jar.name}">
      <manifest>
        <attribute name="PathVisio-Plugin-Class" value="org.pathvisio.plugins.HelloPlugin"/>
      </manifest>
      <fileset dir="build" includes="**/*.class"/>
    </jar>
  </target>

If the PluginManager? can't find the PathVisio-Plugin-Class attribute in the manifest, it will scan the jar file for classes that implement the Plugin interface. This can take more than a few seconds on a large jar file.

Plugin libraries

If your plugin needs libraries that are not included in PathVisio, you have to unjar them and package them in the plugin jar. You can use the <unjar> ant task. For example add this code before the <jar> task call:

<unjar dest="${class.dir}">
	<fileset dir="${lib.dir}" includes="**/*.jar"/>
</unjar>

Recipies for plugin development: HOWTO…

HOWTO add a menu action

The easiest way is to register your action in your plugins' init method:

        public void init(PvDesktop desktop) 
        {
                // register our action in the "Help" menu.
                desktop.registerMenuAction ("Help", helloAction);
        }

Of course you need to define your action as well. A simple action looks like this:

        /**
         * Display a welcome message when this action is triggered. 
         */
        private class HelloAction extends AbstractAction
        {
                HelloAction()
                {
                        // The NAME property of an action is used as 
                        // the label of the menu item
                        putValue (NAME, "Welcome message");
                }
                
                /**
                 *  called when the user selects the menu item
                 */
                public void actionPerformed(ActionEvent arg0) 
                {
                        JOptionPane.showMessageDialog(
                                        desktop.getFrame(), 
                                        "Hello World"); 
                }
        }

See HelloWorldPlugin for the complete listing.

HOWTO add a toolbar action

You can add either an javax.swing.Action or a javax.swing.Component and add it to the Toolbar.

        // add our action to the toolbar
        desktop.getSwingEngine().getApplicationPanel().addToToolbar(toolbarAction);

See the complete example: ExToolbar.java See also VisualizationPlugin?

HOWTO add an action to the right-click menu

You can hook into the context menu that appears when you right-click on a Pathway element

                // register a hook so we can modify the right-click menu 
                desktop.addPathwayElementMenuHook(new PathwayElementMenuHook()
                {
                        /**
                         * This method is called whenever the user right-clicks
                         * on an element in the Pathway.
                         * VPathwayElement contains the object that was clicked on.
                         */
                        public void pathwayElementMenuHook(VPathwayElement e, JPopupMenu menu) 
                        {
                                // We instantiate an Action
                                ShowInfoAction showInfoAction = new ShowInfoAction();
                                
                                // pass the clicked element to the action object
                                showInfoAction.setElement(e);
                                
                                // Insert action into the menu.
                                // NB: this is optional, we can choose e.g.
                                // to insert only when the clicked element is a certain type.
                                menu.add (showInfoAction);
                        }
                }

Where ShowInfoAction? is defined as

        private class ShowInfoAction extends AbstractAction
        {
                private VPathwayElement elt;
                
                public ShowInfoAction()
                {
                        // This will be the label of the pop up menu item.
                        putValue (NAME, "What class is this?");
                }
                
                /**
                 * This should be called before the action is triggered 
                 */
                public void setElement (VPathwayElement anElt)
                {
                        elt = anElt;
                }
                
                public void actionPerformed(ActionEvent arg0) 
                {
                        // Display a message with the actual class 
                        JOptionPane.showMessageDialog(desktop.getFrame(), 
                                        "You clicked a " + elt.getClass(), "Class Information",
                                        JOptionPane.INFORMATION_MESSAGE);
                }
        }

See ExContextMenu.java

See also the  PPPiP plugin for a real-world example.

HOWTO create a sidebar panel

        public void init(PvDesktop desktop) 
        {
                this.desktop = desktop;
                
                // create a new panel to show in the side bar
                JPanel mySideBarPanel = new JPanel ();
                mySideBarPanel.setLayout (new BorderLayout());
                mySideBarPanel.add (new JLabel ("Hello SideBar"), BorderLayout.CENTER);
                
                // get a reference to the sidebar
                JTabbedPane sidebarTabbedPane = desktop.getSideBarTabbedPane();
                
                // add or panel with a given Title
                sidebarTabbedPane.add("Title", mySideBarPanel);
        }

(See ExSidepanel.java)

The  chempaint plugin uses this technique to display structural formulas for metabolites in the side panel.

HOWTO add a tab to the pathway element double-click dialog

Step 1: create a new class that extends PathwayElementPanel?. This panel is going to be added to the dialog. The constructor should initialize GUI widgets and add them with this.add(widget). You should also override the refresh() method, to update all widgets with the contents of the PathwayElement? that is clicked on. That PathwayElement? can be gotten using the protected getInput() method.

For example:

        private class LinkPanel extends PathwayElementPanel implements DocumentListener
        {
                // text field containing the link
                private JTextField txtRef;
                
                public LinkPanel()
                {
                        txtRef = new JTextField(40);
                        txtRef.getDocument().addDocumentListener(this);
                        add(txtRef);
                }
                
                @Override
                public void refresh()
                {
                        txtRef.setText (getInput().getHref());
                }

                @Override
                public void changedUpdate(DocumentEvent arg0)
                {
                        getInput().setHref(txtRef.getText());
                }

                @Override
                public void insertUpdate(DocumentEvent arg0)
                {
                        getInput().setHref(txtRef.getText());
                }

                @Override
                public void removeUpdate(DocumentEvent arg0)
                {
                        getInput().setHref(txtRef.getText());
                }
        }

Step 2: Implement PopupDialogHook? and add it to the PopupDialogHandler?. In the following example, the hook is only added if the PathwayElement? is of type LABEL.

                // define the new hook to be added
                desktop.getSwingEngine().getPopupDialogHandler().addHook(new PopupDialogHook()
                {
                        @Override
                        public void popupDialogHook(PathwayElement e, PathwayElementDialog dlg)
                        {
                                // check if it is a label first
                                if (e.getObjectType() == ObjectType.LABEL)
                                        // add the LinkPanel, which is defined below.
                                        dlg.addPathwayElementPanel("Link", new LinkPanel());
                        }
                });

(See ExPopupDialog.java)

HOWTO add a new experimental data visualization style

You have to extend the VisualizationMethod? class. Then you can register new methods as follows:

    //Register the visualization methods
    VisualizationMethodRegistry reg =
        desktop.getVisualizationMethodRegistry();
        
        reg.registerMethod(
             ColorByExpression.class.toString(),
             new VisualizationMethodProvider() {
                 public VisualizationMethod create(Visualization v, String registeredName) {
                     return new ColorByExpression(v, registeredName, desktop.getGexManager());
                 }
             }
        );

See the VisualizationPlugin for an example.

HOWTO add a new pathway importer / exporter

You should start by creating a class that implements either the PathwayImport? or PathwayExport? interface, or both.

Then you register them like so in your init() method:

        // instantiate TextLinesImporter, our own importer, and register it
        desktop.getSwingEngine().getEngine().addPathwayImporter(new TextLinesImporter());

        // here we register an instance of our exporter
        desktop.getSwingEngine().getEngine().addPathwayExporter(new BibliographyExporter());

If the plug-in is loaded correctly, you can access the importer / exporter by clicking File->Import or File->Export. It will now show up in file type drop-down in the file chooser dialog.

See ExImporter.java and ExExporter.java

See also the HtmlExport? plugin for a real-world example:  http://svn.bigcat.unimaas.nl/pvplugins/trunk/htmlexport

HOWTO add custom shapes

TODO

HOWTO save session preferences

The PreferenceManager? provides a way to save values from one session to the next. For example, to add a measure of user-friendliness, we can use the PreferenceManager? to save the current directory of a JFileChooser, so that the next time we open it, it will open in the last used directory. Of all possible directories, the last used directory is the most likely to contain the file the user is looking for.

In order to store a preference, we have to instantiate a class that implements the Preference interface. The Preference interface provides both a name (which will be used as key in the properties file) and a default value. If you want to define multiple Preferences for your plugin, the easiest way is to define a enum, although there are also other ways to do this. For example:

        enum ExPreference implements Preference
        {
                EXAMPLE_LAST_OPENED_DIR (System.getProperty("user.home"));

                ExPreference (String defaultValue) 
                {
                        this.defaultValue = defaultValue;
                }
                
                private String defaultValue;
                
                public String getDefault() {
                        return defaultValue;
                }               
        }

To get and set a value, use one of the PreferenceManager?.getXxxx() or PreferenceManager?.setXxxx() methods. The preferences will be automatically loaded and saved by the PathVisio framework to a properties file (Usually ~/.PathVisio/.PathVisio)

Here is an example:

        JFileChooser jfc = new JFileChooser();
        // set current directory to what was stored in the preferences
        // default: home directory.
        jfc.setCurrentDirectory(
                        PreferenceManager.getCurrent().getFile(ExPreference.EXAMPLE_LAST_OPENED_DIR));
        int status = jfc.showOpenDialog(desktop.getFrame());
        if(status == JFileChooser.APPROVE_OPTION)
        {
                // save current directory of jfc.
                // next time will be opened in same location.
                PreferenceManager.getCurrent().setFile(ExPreference.EXAMPLE_LAST_OPENED_DIR,
                                jfc.getCurrentDirectory());
        }

See ExPreferences.java See also the ZScore Plugin for a real-world example

HOWTO add a panel to the preference dialog

The previous HOWTO explains how to use the PreferenceManager? to save session state variables. Of course you can also use PreferenceManager? to save actual user preferences. Saving and retrieving user preferences is exactly the same, the only difference is that you should make them available in the Preferences Dialog that you can access in the edit menu.

All you have to do is use PreferencesDlg?.builder() to obtain a PreferencePanel?.Builder() object, to which you add the Preference's one by one. Finally call the build() method when it's done and add it to the dialog with dlg.addPanel(), Here is a simple example that defines a Color preference and an integer preference:

        PreferencesDlg dlg = desktop.getPreferencesDlg();
        
        dlg.addPanel("Example Plugin", 
                        dlg.builder()
                                .colorField(ExPreference.EXAMPLE_COLOR, "Example Color")
                                .integerField(ExPreference.EXAMPLE_INT, "Example Integer between 0 and 100", 0, 100)
                                .build()
                        );

See ExPreferences.java

HOWTO add information to the backpage

TODO

HOWTO add a a custom property to the properties tab

You can store custom information in the pathway using dynamic properties. A plugin can register a custom dynamic property to the properties tab, so users can edit this property for the selected pathway element. To do this, your plugin needs to implement the property interface for the property you want to add:

static final Property MY_GENERAL_PROPERTY = new Property () {
        //A unique id for the property
        public String getId() {
                return "org.pathvisio.example.MyProperty";
        }
        //The description, wiill be used as tooltip in the property panel
        public String getDescription() {
                return "This is an example property";
        }
        //The name the user sees in the property panel
        public String getName() {
                return "Example property";
        }
        //The data type of the property
        public PropertyType getType() {
                return StaticPropertyType.STRING;
        }
        public boolean isCollection() {
                return false;
        }
};

And then register the property to the  PropertyManager and  PropertyDisplayManager:

PropertyManager.registerProperty(MY_GENERAL_PROPERTY);
PropertyDisplayManager.registerProperty(MY_GENERAL_PROPERTY);

This will make the property appear in the property panel for any pathway element. If you want the property to apply to a specific pathway element only, you can set the scope of the property after registering it. For example, to register a property that only applies to elements with type DataNode? and Label:

PropertyManager.registerProperty(MY_SPECIFIC_PROPERTY);
PropertyDisplayManager.registerProperty(MY_SPECIFIC_PROPERTY);
PropertyDisplayManager.setPropertyScope(
        MY_SPECIFIC_PROPERTY, 
        EnumSet.of(ObjectType.DATANODE, ObjectType.LABEL)
);

For a complete example of this plugin, please see ExDynamicPropertyPanel.java

Publishing your plugin

HOWTO unit test your plugin

  • How to send your plugin to us...

Plugins in other programming languages

Plugins are not restricted to Java: they can be written in any programming language that can run on the Java Virtual Machine (JVM). So this includes Jython, Groovy and JRuby.