21272
Comment:
|
32810
Partial description on how to write a plug-in that registers one or more attribute functions.
|
Deletions are marked like this. | Additions are marked like this. |
Line 1: | Line 1: |
[[TableOfContents([2])]] |
|
Line 4: | Line 2: |
<<TableOfContents()>> == Introduction == |
|
Line 8: | Line 10: |
1. A class, which extends CytoscapePlugin. This is the entry point to Cytoscape. 1. A manifest file, explicitly list the class, which extended CytoscapePlugin class |
1. A class, which extends !CytoscapePlugin. This is the entry point to Cytoscape. 1. A manifest file, explicitly list the class, which extended !CytoscapePlugin class |
Line 17: | Line 19: |
#!java | |
Line 25: | Line 28: |
}}} Download a sample plugin, [http://chianti.ucsd.edu/svn/csplugins/trunk/ucsd/pwang/wikizip/tutorial01.tar Here]. [[BR]] |
}}} Download a sample plugin, [[http://chianti.ucsd.edu/svn/csplugins/trunk/ucsd/pwang/wikizip/tutorial01.zip|Here]]. <<BR>> |
Line 34: | Line 38: |
#!java | |
Line 36: | Line 41: |
}}} The toolbarAction must extend the class cytoscape.util.CytoscapeAction and its method isInToolBar()returns true. |
}}} The toolbarAction must extend the class cytoscape.util.!CytoscapeAction and its method isInToolBar()returns true. |
Line 42: | Line 48: |
#!java | |
Line 43: | Line 50: |
}}} Download a sample plugin, [http://chianti.ucsd.edu/svn/csplugins/trunk/ucsd/pwang/wikizip/tutorial02.tar Here]. |
}}} Download a sample plugin, [[http://chianti.ucsd.edu/svn/csplugins/trunk/ucsd/pwang/wikizip/tutorial02.zip|Here]]. |
Line 47: | Line 55: |
Same as adding an image icon to the toolbar, the difference is that its method “isInMenuBar()” in CytoscapeAction should return true. Download a sample plugin, [http://chianti.ucsd.edu/svn/csplugins/trunk/ucsd/pwang/wikizip/tutorial03.tar Here]. [[BR]] |
Same as adding an image icon to the toolbar, the difference is that its method “isInMenuBar()” in !CytoscapeAction should return true. Download a sample plugin, [[http://chianti.ucsd.edu/svn/csplugins/trunk/ucsd/pwang/wikizip/tutorial03.zip|Here]]. <<BR>> |
Line 53: | Line 60: |
A general procedure to create a network is, first create a bunch of CyNodes using the Cytoscape.getCyNode() method, then create edges using those nodes with Cytoscape.getCyEdge() method. Then call Cytoscape.createNetwork() with the list of nodes and edges just created. | A general procedure to create a network is, first create a bunch of !CyNodes using the Cytoscape.getCyNode() method, then create edges using those nodes with Cytoscape.getCyEdge() method. Then call Cytoscape.createNetwork() with the list of nodes and edges just created. |
Line 58: | Line 65: |
#!java | |
Line 78: | Line 86: |
}}} To remove a node, we use the RootGraphIndex of the Node as follows. Note that when we remove a node, we have the option to remove it from the invisible rootGrpah or just hide it from current network. {{{ |
}}} To remove a node, we use the !RootGraphIndex of the Node as follows. Note that when we remove a node, we have the option to remove it from the invisible rootGrpah or just hide it from current network. {{{ #!java |
Line 83: | Line 93: |
}}} |
}}} |
Line 87: | Line 98: |
#!java | |
Line 88: | Line 100: |
}}} Download a sample plugin, [http://chianti.ucsd.edu/svn/csplugins/trunk/ucsd/pwang/wikizip/tutorial04.tar Here]. |
}}} Download a sample plugin, [[http://chianti.ucsd.edu/svn/csplugins/trunk/ucsd/pwang/wikizip/tutorial04.zip|Here]]. |
Line 96: | Line 108: |
#!java | |
Line 98: | Line 111: |
}}} |
}}} |
Line 102: | Line 116: |
#!java | |
Line 104: | Line 119: |
}}} |
}}} |
Line 108: | Line 124: |
#!java | |
Line 109: | Line 126: |
}}} Download a sample plugin, [http://chianti.ucsd.edu/svn/csplugins/trunk/ucsd/pwang/wikizip/tutorial05.tar Here]. [[BR]] |
}}} Download a sample plugin, [[http://chianti.ucsd.edu/svn/csplugins/trunk/ucsd/pwang/wikizip/tutorial05.zip|Here]]. <<BR>> |
Line 118: | Line 134: |
#!java | |
Line 126: | Line 143: |
}}} Download a sample plugin, [http://chianti.ucsd.edu/svn/csplugins/trunk/ucsd/pwang/wikizip/tutorial06.tar Here]. [[BR]] |
}}} Download a sample plugin, [[http://chianti.ucsd.edu/svn/csplugins/trunk/ucsd/pwang/wikizip/tutorial06.zip|Here]]. <<BR>> |
Line 132: | Line 151: |
#!java | |
Line 145: | Line 165: |
}}} Download a sample plugin, [http://chianti.ucsd.edu/svn/csplugins/trunk/ucsd/pwang/wikizip/tutorial07.tar Here]. [[BR]] |
}}} Download a sample plugin, [[http://chianti.ucsd.edu/svn/csplugins/trunk/ucsd/pwang/wikizip/tutorial07.zip|Here]]. <<BR>> |
Line 155: | Line 175: |
#!java | |
Line 165: | Line 186: |
}}} Download a sample plugin, [http://chianti.ucsd.edu/svn/csplugins/trunk/ucsd/pwang/wikizip/tutorial08.tar Here]. [[BR]] |
}}} Download a sample plugin, [[http://chianti.ucsd.edu/svn/csplugins/trunk/ucsd/pwang/wikizip/tutorial08.zip|Here]]. <<BR>> |
Line 173: | Line 196: |
#!java | |
Line 178: | Line 202: |
}}} Download a sample plugin, [http://chianti.ucsd.edu/svn/csplugins/trunk/ucsd/pwang/wikizip/tutorial09.tar Here]. |
}}} Download a sample plugin, [[http://chianti.ucsd.edu/svn/csplugins/trunk/ucsd/pwang/wikizip/tutorial09.zip|Here]]. |
Line 185: | Line 210: |
#!java | |
Line 189: | Line 215: |
}}} |
}}} |
Line 193: | Line 220: |
#!java | |
Line 195: | Line 223: |
}}} Download a sample plugin, [http://chianti.ucsd.edu/svn/csplugins/trunk/ucsd/pwang/wikizip/tutorial10.tar Here]. [[BR]] |
}}} Download a sample plugin, [[http://chianti.ucsd.edu/svn/csplugins/trunk/ucsd/pwang/wikizip/tutorial10.zip|Here]]. <<BR>> |
Line 203: | Line 233: |
#!java | |
Line 205: | Line 236: |
}}} Download a sample plugin, [http://chianti.ucsd.edu/svn/csplugins/trunk/ucsd/pwang/wikizip/tutorial11.tar Here]. |
}}} Download a sample plugin, [[http://chianti.ucsd.edu/svn/csplugins/trunk/ucsd/pwang/wikizip/tutorial11.zip|Here]]. |
Line 212: | Line 244: |
#!java | |
Line 219: | Line 252: |
}}} |
}}} |
Line 223: | Line 257: |
#!java | |
Line 228: | Line 263: |
}}} Download a sample plugin, [http://chianti.ucsd.edu/svn/csplugins/trunk/ucsd/pwang/wikizip/tutorial12.tar Here]. [[BR]] |
}}} Download a sample plugin, [[http://chianti.ucsd.edu/svn/csplugins/trunk/ucsd/pwang/wikizip/tutorial12.zip|Here]]. <<BR>> |
Line 237: | Line 274: |
. WebServiceClientManager.registerClient(My_NCBIClient.getClient()); Download a sample plugin, [http://chianti.ucsd.edu/svn/csplugins/trunk/ucsd/pwang/wikizip/tutorial13.tar Here]. |
{{{ #!java WebServiceClientManager.registerClient(My_NCBIClient.getClient()); }}} Download a sample plugin, [[http://chianti.ucsd.edu/svn/csplugins/trunk/ucsd/pwang/wikizip/tutorial13.zip|Here]]. |
Line 244: | Line 285: |
#!java | |
Line 246: | Line 288: |
}}} To create a VisualStyle for a network view, an easy way is to use VisualStyleBuilder. The following code snippet will create a VisualStyle with name “myVisualStyle”. After the visualStyle is created, it will appear in the comboBox of VizMapper main panel. {{{ // Create a visual style "myVisualStyle" String styleName = "myVisualStyle"; VisualStyleBuilder graphStyle = new VisualStyleBuilder(styleName, false); graphStyle.setNodeSizeLocked(false); // set some visual property for two nodes graphStyle.addProperty(node1.getIdentifier(), VisualPropertyType.NODE_WIDTH, "30"); graphStyle.addProperty(node1.getIdentifier(), VisualPropertyType.NODE_FILL_COLOR, "#E1E1E1"); graphStyle.addProperty(node1.getIdentifier(), VisualPropertyType.NODE_SHAPE, NodeShape.DIAMOND.getShapeName()); graphStyle.addProperty(node2.getIdentifier(), VisualPropertyType.NODE_WIDTH, "80"); graphStyle.addProperty(node2.getIdentifier(), VisualPropertyType.NODE_FILL_COLOR, "#0000E1"); graphStyle.addProperty(node2.getIdentifier(), VisualPropertyType.NODE_SHAPE, NodeShape.TRIANGLE.getShapeName()); // Create the visual style graphStyle.buildStyle(); // Set the background color for this visual style GlobalAppearanceCalculator gac = Cytoscape.getVisualMappingManager().getVisualStyle().getGlobalAppearanceCalculator(); gac.setDefaultBackgroundColor(Color.red); // Refresh the view Cytoscape.getCurrentNetworkView().redrawGraph(false, true); }}} Download a sample plugin, [http://chianti.ucsd.edu/svn/csplugins/trunk/ucsd/pwang/wikizip/tutorial14.tar Here]. [[BR]] |
}}} To create a VisualStyle for a network view, an easy way is to use VisualStyleBuilder. The following code snippet will create a VisualStyle with name “Example Visual Style”. {{{ #!java public static final String vsName = "Example Visual Style"; VisualStyle createVisualStyle(CyNetwork network) { NodeAppearanceCalculator nodeAppCalc = new NodeAppearanceCalculator(); EdgeAppearanceCalculator edgeAppCalc = new EdgeAppearanceCalculator(); GlobalAppearanceCalculator globalAppCalc = new GlobalAppearanceCalculator(); // Passthrough Mapping - set node label //PassThroughMapping pm = new PassThroughMapping(new String(), "attr2"); PassThroughMapping pm = new PassThroughMapping(new String(), "attr2asdf"); Calculator nlc = new BasicCalculator("Example Node Label Calculator", pm, VisualPropertyType.NODE_LABEL); nodeAppCalc.setCalculator(nlc); // Discrete Mapping - set node shapes DiscreteMapping disMapping = new DiscreteMapping(NodeShape.RECT, ObjectMapping.NODE_MAPPING); disMapping.setControllingAttributeName("attr1", network, false); disMapping.putMapValue(new Integer(1), NodeShape.DIAMOND); disMapping.putMapValue(new Integer(2), NodeShape.ELLIPSE); disMapping.putMapValue(new Integer(3), NodeShape.TRIANGLE); Calculator shapeCalculator = new BasicCalculator("Example Node Shape Calculator", disMapping, VisualPropertyType.NODE_SHAPE); nodeAppCalc.setCalculator(shapeCalculator); // Continuous Mapping - set node color ContinuousMapping continuousMapping = new ContinuousMapping(Color.WHITE, ObjectMapping.NODE_MAPPING); continuousMapping.setControllingAttributeName("attr3", network, false); Interpolator numToColor = new LinearNumberToColorInterpolator(); continuousMapping.setInterpolator(numToColor); Color underColor = Color.GRAY; Color minColor = Color.RED; Color midColor = Color.WHITE; Color maxColor = Color.GREEN; Color overColor = Color.BLUE; // Create boundary conditions less than, equals, greater than BoundaryRangeValues bv0 = new BoundaryRangeValues(underColor, minColor, minColor); BoundaryRangeValues bv1 = new BoundaryRangeValues(midColor, midColor, midColor); BoundaryRangeValues bv2 = new BoundaryRangeValues(maxColor, maxColor, overColor); // Set the attribute point values associated with the boundary values continuousMapping.addPoint(0.0, bv0); continuousMapping.addPoint(1.0, bv1); continuousMapping.addPoint(2.0, bv2); Calculator nodeColorCalculator = new BasicCalculator("Example Node Color Calc", continuousMapping, VisualPropertyType.NODE_FILL_COLOR); nodeAppCalc.setCalculator(nodeColorCalculator); // Discrete Mapping - Set edge target arrow shape DiscreteMapping arrowMapping = new DiscreteMapping(ArrowShape.NONE, ObjectMapping.EDGE_MAPPING); arrowMapping.setControllingAttributeName("interaction", network, false); arrowMapping.putMapValue("pp", ArrowShape.ARROW); arrowMapping.putMapValue("pd", ArrowShape.CIRCLE); Calculator edgeArrowCalculator = new BasicCalculator("Example Edge Arrow Shape Calculator", arrowMapping, VisualPropertyType.EDGE_TGTARROW_SHAPE); edgeAppCalc.setCalculator(edgeArrowCalculator); // Create the visual style VisualStyle visualStyle = new VisualStyle(vsName, nodeAppCalc, edgeAppCalc, globalAppCalc); return visualStyle; } }}} The following snippet of code shows how to use the above createVisualStyle(network) method. After the visualStyle is created, it will appear in the comboBox of VizMapper main panel. {{{ #!java // get the network and view CyNetwork network = Cytoscape.getCurrentNetwork(); CyNetworkView networkView = Cytoscape.getCurrentNetworkView(); // get the VisualMappingManager and CalculatorCatalog VisualMappingManager manager = Cytoscape.getVisualMappingManager(); CalculatorCatalog catalog = manager.getCalculatorCatalog(); // check to see if a visual style with this name already exists VisualStyle vs = catalog.getVisualStyle(vsName); if (vs == null) { // if not, create it and add it to the catalog vs = createVisualStyle(network); catalog.addVisualStyle(vs); } networkView.setVisualStyle(vs.getName()); // not strictly necessary // actually apply the visual style manager.setVisualStyle(vs); networkView.redrawGraph(true,true); }}} Download a sample plugin, [[http://chianti.ucsd.edu/svn/csplugins/trunk/ucsd/pwang/wikizip/tutorial14.zip|Here]]. <<BR>> |
Line 277: | Line 406: |
To map node degree to node color, first we need to define a calculator with continuous mapping for the visual attribute NODE_FILL_COLOR. Then set the calculator to the NodeAppearanceCalculator of the visual style to be applied. | To map node degree to node color, first we need to define a calculator with continuous mapping for the visual attribute NODE_FILL_COLOR. Then set the calculator to the !NodeAppearanceCalculator of the visual style to be applied. |
Line 280: | Line 409: |
{{{ |
{{{ #!java |
Line 284: | Line 415: |
// Set the calculator to the visualStyle | // Set the calculator to the visualStyle |
Line 286: | Line 417: |
}}} |
}}} |
Line 289: | Line 420: |
{{{ |
{{{ #!java |
Line 292: | Line 425: |
Line 299: | Line 432: |
Line 305: | Line 438: |
Line 309: | Line 442: |
Line 313: | Line 446: |
Line 315: | Line 448: |
BasicCalculator myCalculator = new BasicCalculator("My degree calcualtor", cm, VisualPropertyType.NODE_FILL_COLOR); }}} Download a sample plugin, [http://chianti.ucsd.edu/svn/csplugins/trunk/ucsd/pwang/wikizip/tutorial15.tar Here]. [[BR]] |
BasicCalculator myCalculator = new BasicCalculator("My degree calcualtor", cm, VisualPropertyType.NODE_FILL_COLOR); }}} Download a sample plugin, [[http://chianti.ucsd.edu/svn/csplugins/trunk/ucsd/pwang/wikizip/tutorial15.zip|Here]]. <<BR>> |
Line 323: | Line 456: |
[http://chianti.ucsd.edu/svn/csplugins/trunk/ucsd/pwang/wikizip/tutorial16.tar Here]. [[BR]] |
Cytoscape supported import/export of visual properties file. They can be found at File --> Import --> Vizmap Property File and File--> Export --> Vizmap Property File. To load a vizmap property file programmatically, we can reuse most of the code in the Cytoscape core class “cytoscape.actions.!ImportVizmapAction”. Cytoscape already had build-in class to handle the import of Vzmap property file, all we need is to fire a VIZMAP_LOADED event as following {{{ #!java Cytoscape.firePropertyChange(Cytoscape.VIZMAP_LOADED, null, file.getAbsolutePath()); }}} After the event is fired, a dialog box will prompt the Import of Vizmap Property File. Download a sample plugin, [[http://chianti.ucsd.edu/svn/csplugins/trunk/ucsd/pwang/wikizip/tutorial16.zip|Here]]. <<BR>> |
Line 327: | Line 470: |
Line 331: | Line 473: |
{{{ |
{{{ #!java |
Line 333: | Line 477: |
}}} One of the methods that a CyLayoutAlgorithm must implement is a getSettings() method, which returns an object of LayoutProperties that winds up under the Settings menu. Layout parameters may be supplied with the LayoutProperties. For example, the following statement defines a parameter in the LayoutProperties – name, description, data type and value. {{{ |
}}} One of the methods that a !CyLayoutAlgorithm must implement is a getSettings() method, which returns an object of !LayoutProperties that winds up under the Settings menu. Layout parameters may be supplied with the LayoutProperties. For example, the following statement defines a parameter in the !LayoutProperties – name, description, data type and value. {{{ #!java |
Line 338: | Line 485: |
}}} Download a sample plugin, [http://chianti.ucsd.edu/svn/csplugins/trunk/ucsd/pwang/wikizip/tutorial17.tar Here]. [[BR]] == How to customize node graphics? == Download a sample plugin, [http://chianti.ucsd.edu/svn/csplugins/trunk/ucsd/pwang/wikizip/tutorial18.tar Here]. [[BR]] |
}}} Download a sample plugin, [[http://chianti.ucsd.edu/svn/csplugins/trunk/ucsd/pwang/wikizip/tutorial17.zip|Here]]. <<BR>> |
Line 352: | Line 492: |
Download a sample plugin, [http://chianti.ucsd.edu/svn/csplugins/trunk/ucsd/pwang/wikizip/tutorial19.tar Here]. [[BR]] |
A class of CyGroupViewer must implement the !CyGroupViewer interface and it should register with the !CyGroupManager. {{{ #!java MyGroupViewer myView = new MyGroupViewer(); // Register with CyGroup CyGroupManager.registerGroupViewer(myView); }}} Download a sample plugin, [[http://chianti.ucsd.edu/svn/csplugins/trunk/ucsf/scooter/sampleGroupViewer|Here]]. <<BR>> |
Line 359: | Line 506: |
To add a component to content menu of a node view, first we need create a listener class, which implements the interface NodeContextMenuListener, then register this listener through the network view. Here is how the listener class looks like, {{{ |
To add a component to content menu of a node view, first we need create a listener class, which implements the interface !NodeContextMenuListener, then register this listener through the network view. Here is what the listener class looks like, {{{ #!java |
Line 365: | Line 513: |
public void addNodeContextMenuItems(NodeView nodeView, JPopupMenu menu) { JMenuItem myMenuItem = new JMenuItem("MyNodeMenuItem"); myMenuItem.addActionListener(new MyNodeAction(nodeView)); if (menu == null) { menu = new JPopupMenu(); } menu.add(myMenuItem); } } }}} The method addNodeContentMenuItems() takes two arguments, NodeView and JPopupMenu. The JPopupMenu will be the one show up when user right-click on the NodeView on canvas. The sample code above will add a new menu item “MyNodeMenuItem” in the popupMenu. The MyNodeAction class, which implements ActionListener, will be the class performs the action after the menu item is clicked. The NodeView parameter to the listener will provide the reference to the Node via NodeView.getNode() method and take whatever action desired. |
public void addNodeContextMenuItems(NodeView nodeView, JPopupMenu menu) { JMenuItem myMenuItem = new JMenuItem("MyNodeMenuItem"); myMenuItem.addActionListener(new MyNodeAction(nodeView)); if (menu == null) { menu = new JPopupMenu(); } menu.add(myMenuItem); } } }}} The method addNodeContentMenuItems() takes two arguments, !NodeView and !JPopupMenu. The !JPopupMenu will be the one show up when user right-click on the NodeView on canvas. The sample code above will add a new menu item “!MyNodeMenuItem” in the popupMenu. The !MyNodeAction class, which implements !ActionListener, will be the class performs the action after the menu item is clicked. The !NodeView parameter to the listener will provide the reference to the Node via !NodeView.getNode() method and take whatever action desired. |
Line 380: | Line 529: |
{{{ |
{{{ #!java |
Line 382: | Line 533: |
Cytoscape.getCurrentNetworkView().addNodeContextMenuListener(l); }}} |
Cytoscape.getCurrentNetworkView().addNodeContextMenuListener(l); }}} |
Line 387: | Line 539: |
{{{ import browser.AttributeBrowserPlugin; |
{{{ #!java import browser.AttributeBrowserPlugin; |
Line 394: | Line 548: |
}}} |
}}} |
Line 397: | Line 552: |
Download a sample plugin, [http://chianti.ucsd.edu/svn/csplugins/trunk/ucsd/pwang/wikizip/tutorial20.tar Here]. [[BR]] |
Download a sample plugin, [[http://chianti.ucsd.edu/svn/csplugins/trunk/ucsd/pwang/wikizip/tutorial20.zip|Here]]. <<BR>> |
Line 401: | Line 557: |
Line 403: | Line 558: |
{{{ |
{{{ #!java |
Line 411: | Line 568: |
File global_prop_file = CytoscapeInit.getConfigFile("tutorial21.props"); } }}} |
File global_prop_file = CytoscapeInit.getConfigFile("tutorial21.props"); } }}} |
Line 415: | Line 573: |
{{{ |
{{{ #!java |
Line 418: | Line 578: |
}}} Download a sample plugin, [http://chianti.ucsd.edu/svn/csplugins/trunk/ucsd/pwang/wikizip/tutorial21.tar Here]. [[BR]] |
}}} Download a sample plugin, [[http://chianti.ucsd.edu/svn/csplugins/trunk/ucsd/pwang/wikizip/tutorial21.zip|Here]]. <<BR>> |
Line 423: | Line 585: |
If a task will take a long time to finish, it is better to use Cytoscape task manager to run it in a separate thread and show its progress to the user. With the Task manager, it is possible to cancel the task, and the window won’t freeze. Download a sample plugin, [http://chianti.ucsd.edu/svn/csplugins/trunk/ucsd/pwang/wikizip/tutorial22.tar Here]. |
If a task will take a long time to finish, it is better to use Cytoscape task manager to run it in a separate thread and show its progress to the user. With the Task manager, it is possible to cancel the task, and the window won’t freeze. Download a sample plugin, [[http://chianti.ucsd.edu/svn/csplugins/trunk/ucsd/pwang/wikizip/tutorial22.zip|Here]]. <<BR>> == How to add new attribute functions via a Cytoscape plug-in == The easiest part is writing the actual plug-in class, which look similar to this: {{{ #!java package cytoscape.tutorial23; import cytoscape.data.eqn_attribs.AttribParser; import cytoscape.data.eqn_attribs.Parser; import cytoscape.plugin.CytoscapePlugin; public class Tutorial23 extends CytoscapePlugin { public Tutorial23() { final AttribParser theParser = Parser.getParser(); theParser.registerFunction(new IXor()); } } }}} Here we register a new built-in function called IXor. You can also register multiple built-in functions if you prefer. The next part is creating one class each for every new built-in. Each such class has to implement the ''cytoscape.data.eqn_attribs.!AttribFunction'' interface. The easiest way to get started is to peruse the existing built-ins in ''.../src/cytoscape/data/eqn_attribs/builtins/'' and look for a function with a similar or identical argument list. If you can't find one it may still be instructive to read through the implementation of a couple of existing functions. The most trivial to implement methods are ''getName()'', ''getFunctionSummary()'', and ''getUsageDescription()''. {{{ #!java /** * Used to parse the function string. This name is treated in a case-insensitive manner! * @returns the name by which you must call the function when used in an attribute equation. */ public String getName() { return "IXOR"; } /** * Used to provide help for users. * @returns a description of what this function does */ public String getFunctionSummary() { return "Returns an integer value that is the exclusive-or of 2 other integer values."; } /** * Used to provide help for users. * @returns a description of how to use this function */ public String getUsageDescription() { return "Call this with \"IXOR(integer1,integer2)\""; } }}} The next method is still simple but already touches on the one area that is somewhat complicated in attribute functions: data types and data type conversions! {{{ #!java public Class getReturnType() { return Long.class; } }}} Our example function is supposed to return an integer value. Integers in equation functions are represented as instances of class ''java.lang.Long''. Therefore it is imperative '''not''' to return ''Integer.class''. The user of the function can be blissfully unaware of this distinction! Just remember to always substitute ''Long'' for ''Integer'' and you should be fine. The next method ''validateArgTypes()'' is used by the equation compiler to check the validity of the argument types in function call. Basically the compiler passes in the list of argument types for a given call. The method is then supposed to return the type of the result if the argument types are acceptable or ''null'' if the argument types are invalid. {{{ #!java public Class validateArgTypes(final Class[] argTypes) { if (argTypes.length != 2 || (argTypes[0] != Long.class && argTypes[0] != Double.class) || (argTypes[1] != Long.class && argTypes[1] != Double.class)) return null; return Long.class; } }}} Again we are faced with complexity due to types! We have modelled attribute equations after Excel^tm^ which accepts floating point values wherever integers would be the only logical choice. To maintain this compatibility we always have to also accept arguments of type ''Double'' where ''Long'' would make the most sense. If you think that you can avoid this additional complexity for one of your own functions, you are probably mistaken because all constants in equations are treated as floating point values. If you omitted the support of arguments of type ''Double'' you would therefore preclude the passing of any constant values to your function(s). |
Cytoscape Plugin Developer's Tutorial
Contents
-
Cytoscape Plugin Developer's Tutorial
- Introduction
- How to add a tabbed Panel to Control panel?
- How to add an image icon (menu item) to the toolbar?
- How to create a submenu?
- How to create, modify, and destroy a network, nodes, and edges?
- How to create, modify, and destroy a network view?
- How to determine which nodes are currently selected on a network?
- How to handle events from a network (and discuss each event type)?
- How to change the background color of a view?
- How to zoom a network view?
- How to load attribute data?
- How to remove attributes?
- How to use a web service client?
- How to write a web service client?
- How to use the VizMapper programmatically?
- How to apply a continuous color gradient to nodes according to their degree?
- How to load a visual properties file?
- How to write a layout algorithm?
- How to write a Group Viewer?
- How to add components to the node view, edge view, and attribute browser context menus?
- How to save/restore plugin states?
- How to use Cytoscape task monitor to show the progress of my job?
- How to add new attribute functions via a Cytoscape plug-in
Introduction
This tutorial will show some code snippets about how to use Cytoscape APIs on plugin development. Each feature is included in a sample plugin, which can serve as a starting point or template for new plugin developers. The code snippets can also be used as reference for experienced developers.
Cytoscape plugin is usually packaged in a jar file and deployed to the plugins directory of Cytoscape. When Cytoscape starts up and initializes, its plugin manager will look up all the plugins in plugins directory. A plugin should have following three components.
A class, which extends CytoscapePlugin. This is the entry point to Cytoscape.
A manifest file, explicitly list the class, which extended CytoscapePlugin class
A plugin.props file, which list the information about the plugin, such as plugin name, author, release date and more. Although this file is not absolutely required, it is recommended to have one, since plugin manager will need the information to handle the plugin properly. See the page at http://www.cytoscape.org/cgi-bin/moin.cgi/Cytoscape_Plugin_Tutorial for detail about the definition of a plugin.props.
If plugin developer will share and publish their plugins, they are encouraged to submit their plugins to Cytoscape plugin website at http://cytoscape.org/plugins/index.php .
How to add a tabbed Panel to Control panel?
It takes three steps to add a tabbed panel to the control panel.
1 //First, get a handler to the cytoPanel west, which is the control panel
2 CytoPanelImp ctrlPanel = (CytoPanelImp) Cytoscape.getDesktop().getCytoPanel(SwingConstants.WEST);
3
4 //Create a JPanel object (a class extends JPanel, say, MyPanel)
5 MyPanel myPanel = new MyPanel();
6
7 //Add it to the control panel.
8 ctrlPanel.add("myPanel", myPanel);
9
Download a sample plugin, Here.
How to add an image icon (menu item) to the toolbar?
Create a toolbarAction
1 ImageIcon icon = new ImageIcon(getClass().getResource("/tiger.jpg"));
2 MyPluginToolBarAction toolbarAction = new MyPluginToolBarAction(icon, this);
3
The toolbarAction must extend the class cytoscape.util.CytoscapeAction and its method isInToolBar()returns true.
Add the action to Cytoscape toolbar
1 Cytoscape.getDesktop().getCyMenus().addCytoscapeAction((CytoscapeAction) toolbarAction);
2
Download a sample plugin, Here.
How to create a submenu?
Same as adding an image icon to the toolbar, the difference is that its method “isInMenuBar()” in CytoscapeAction should return true.
Download a sample plugin, Here.
How to create, modify, and destroy a network, nodes, and edges?
A general procedure to create a network is, first create a bunch of CyNodes using the Cytoscape.getCyNode() method, then create edges using those nodes with Cytoscape.getCyEdge() method. Then call Cytoscape.createNetwork() with the list of nodes and edges just created.
The following snippet of code will create network with three nodes and three edges. The name of the network is “network1”.
1 //create a network without a view CyNetwork
2 yNetwork = Cytoscape.createNetwork("network1", false);
3
4 CyNode node0 = Cytoscape.getCyNode("rain", true);
5 CyNode node1 = Cytoscape.getCyNode("rainbow", true);
6 CyNode node2 = Cytoscape.getCyNode("rabbit", true);
7 CyNode node3 = Cytoscape.getCyNode("yellow", true);
8
9 cyNetwork.addNode(node0);
10 cyNetwork.addNode(node1);
11 cyNetwork.addNode(node2);
12 cyNetwork.addNode(node3);
13
14 CyEdge edge0 = Cytoscape.getCyEdge(node0, node1, Semantics.INTERACTION, "pp", true);
15 CyEdge edge1 = Cytoscape.getCyEdge(node0, node2, Semantics.INTERACTION, "pp", true);
16 CyEdge edge2 = Cytoscape.getCyEdge(node0, node3, Semantics.INTERACTION, "pp", true);
17
18 cyNetwork.addEdge(edge0);
19 cyNetwork.addEdge(edge1);
20 cyNetwork.addEdge(edge2);
21
To remove a node, we use the RootGraphIndex of the Node as follows. Note that when we remove a node, we have the option to remove it from the invisible rootGrpah or just hide it from current network.
1 cyNetwork.removeNode(node1.getRootGraphIndex(), true);
2
The following statement will destroy a network.
1 Cytoscape.destroyNetwork(cyNetwork);
2
Download a sample plugin, Here.
How to create, modify, and destroy a network view?
A network may or may not have a view. If we already have a network, we can create a view with the following statement.
1 // Create a view for the network CyNetworkView
2 cyView = Cytoscape.createNetworkView(cyNetwork, "MyNetwork");
3
If a node is hidden, the node and all of its incident edges will be hidden.
1 // hide a node
2 cyNetwork.hideNode(node1.getRootGraphIndex());
3
The following statement will destroy a network view.
1 Cytoscape.destroyNetworkView(cyView);
2
Download a sample plugin, Here.
How to determine which nodes are currently selected on a network?
The following snippet of code will get the list of selected nodes in current network and print out the node identifiers in console window.
1 CyNetwork current_network = Cytoscape.getCurrentNetwork();
2 Set selectedNodes = current_network.getSelectedNodes();
3
4 Iterator<Node> it = selectedNodes.iterator();
5 while (it.hasNext()) {
6 Node aNode = (Node) it.next();
7 System.out.println(aNode.getIdentifier());
8 }
9
Download a sample plugin, Here.
How to handle events from a network (and discuss each event type)?
1 // Handle PropertyChangeEvent
2 public void propertyChange(PropertyChangeEvent e) {
3 if (e.getPropertyName().equalsIgnoreCase(Cytoscape.ATTRIBUTES_CHANGED))
4 {
5 System.out.println("Received an event -- Cytoscape.ATTRIBUTES_CHANGED!");
6 }
7 if (e.getPropertyName().equalsIgnoreCase(CytoscapeDesktop.NETWORK_VIEW_FOCUSED))
8 {
9 System.out.println("Received an event -- CytoscapeDesktop.NETWORK_VIEW_FOCUSED!");
10 }
11
12
13 }
14
Download a sample plugin, Here.
How to change the background color of a view?
The network view (DingNetworkView) is composed of three layers of canvases (foreground, network and background). To change the background color of a view, we first get the background canvas, and then set the color for the background. The following snippet of code will change the background color of network view, which has the focus.
1 //Let user choose a color
2 Color bkColor = JColorChooser.showDialog(Cytoscape.getDesktop(), "Please choose a background color", Color.white);
3
4 // Change the background color for current view
5 DingNetworkView theView = (DingNetworkView) Cytoscape.getCurrentNetworkView();
6 DingCanvas backgroundCanvas = theView.getCanvas(DGraphView.Canvas.BACKGROUND_CANVAS);
7 backgroundCanvas.setBackground(bkColor);
8
9 // Refresh the view
10 Cytoscape.getCurrentNetworkView().updateView();
11
Download a sample plugin, Here.
How to zoom a network view?
To zoom a network view, all we need to know is the scale factor. Here is how,
1 // Get reference to the view
2 final CyNetworkView networkView = Cytoscape.getCurrentNetworkView();
3
4 networkView.setZoom(scaleFactor);
5 networkView.updateView();
6
Download a sample plugin, Here.
How to load attribute data?
Attributes are global variables. There are three types of attributes, NODE, EDGE and NETWORK. The following snippet of code shows how to add an attribute for a node – the key value will be the same as the ID of a node.
1 String attributeName = "testAttribute";
2 String AttributeValue = "testValue";
3 CyAttributes cyNodeAttrs = Cytoscape.getNodeAttributes();
4 cyNodeAttrs.setAttribute(node.getIdentifier(), attributeName, AttributeValue);
5
After attributes are loaded, it may be necessary to fire an event to notify other listeners that may have interest to the change.
1 // inform others via property change event.
2 Cytoscape.firePropertyChange(Cytoscape.ATTRIBUTES_CHANGED, null, null);
3
Download a sample plugin, Here.
How to remove attributes?
To remove an attribute, we need to know the attribute name and its type (NODE/EDGE/NETWORK). The following two statements will remove a node attribute.
1 CyAttributes cyNodeAttrs = Cytoscape.getNodeAttributes();
2 cyNodeAttrs.deleteAttribute(attributeName);
3
Download a sample plugin, Here.
How to use a web service client?
After a webservice is registered with the WebServiceManager, it can be used by other plugins. The following code snippet shows how to get the handler to a client by querying the WebServiceManager,
1 WebServiceClient<EUtilsServiceSoap> client =
2 WebServiceClientManager.getClient("tutorial13");
3
4 if (client == null) {
5 System.out.println("Web service client tutorial13 is not found!");
6 return;
7 }
8
If the manager found the webservice client and return it successfully, the client can be used. The client can be used directly by get a handler to the client stub, or by fire webservice event. The following snippet of code will get the database information from NCBI.
1 // eInfo utility returns a list of available databases
2 EUtilsServiceSoap utils = (EUtilsServiceSoap) client.getClientStub();
3
4 // call NCBI EInfo utility, "Available databases from NCBI";
5 EInfoResult res = utils.run_eInfo(new EInfoRequest());
6
Download a sample plugin, Here.
How to write a web service client?
Cytoscape provides a WebServiceManager and a set of webservice APIs. Each webservice should be wrapped into a plugin and register to the WebServiceManager, so that other plugins can easily use the webservice by query the WebServiceManager and get access to the specific web service.
We use NCBI web service “E-Utilities” as an example here. 1. Go to the NCBI webservice site, look at the WSDL http://eutils.ncbi.nlm.nih.gov/entrez/eutils/soap/eutils.wsdl, and generate stubs by following the instructions there. Put all the depended jar files used by the stubs into the lib directory of the plugin, then pack all the generated class into a jar file, say “eutils.jar”. For the jar files in the lib, we can either pack them into “eutils.jar”, or copy them into the lib directory of Cytoscape. To pack all jar into a single plugin jar is preferred, which will make it easy for the distribution of the plugin. 2. Create a webservice client class, say “My_NCBIClient”, which extends WebServiceClientImpl. The client class should implement methods, such as provide an ID and description of the client, and handle the WebserviceEvents. 3. Register the client with the WebServiceClientManager
1 WebServiceClientManager.registerClient(My_NCBIClient.getClient());
2
Download a sample plugin, Here.
How to use the VizMapper programmatically?
All the VisualStyles defined are available through the VisualMappingManager. The following two statements will get the handler of VisualMappingManager and the Set of names of all visual styles there.
1 VisualMappingManager vmm = Cytoscape.getVisualMappingManager();
2 Set<String> names = vmm.getCalculatorCatalog().getVisualStyleNames();
3
To create a VisualStyle for a network view, an easy way is to use VisualStyleBuilder. The following code snippet will create a VisualStyle with name “Example Visual Style”.
1 public static final String vsName = "Example Visual Style";
2 VisualStyle createVisualStyle(CyNetwork network) {
3
4 NodeAppearanceCalculator nodeAppCalc = new NodeAppearanceCalculator();
5 EdgeAppearanceCalculator edgeAppCalc = new EdgeAppearanceCalculator();
6 GlobalAppearanceCalculator globalAppCalc = new GlobalAppearanceCalculator();
7
8
9 // Passthrough Mapping - set node label
10 //PassThroughMapping pm = new PassThroughMapping(new String(), "attr2");
11 PassThroughMapping pm = new PassThroughMapping(new String(), "attr2asdf");
12 Calculator nlc = new BasicCalculator("Example Node Label Calculator",
13 pm, VisualPropertyType.NODE_LABEL);
14 nodeAppCalc.setCalculator(nlc);
15
16
17 // Discrete Mapping - set node shapes
18 DiscreteMapping disMapping = new DiscreteMapping(NodeShape.RECT,
19 ObjectMapping.NODE_MAPPING);
20 disMapping.setControllingAttributeName("attr1", network, false);
21 disMapping.putMapValue(new Integer(1), NodeShape.DIAMOND);
22 disMapping.putMapValue(new Integer(2), NodeShape.ELLIPSE);
23 disMapping.putMapValue(new Integer(3), NodeShape.TRIANGLE);
24
25 Calculator shapeCalculator = new BasicCalculator("Example Node Shape Calculator",
26 disMapping,
27 VisualPropertyType.NODE_SHAPE);
28 nodeAppCalc.setCalculator(shapeCalculator);
29
30
31 // Continuous Mapping - set node color
32 ContinuousMapping continuousMapping = new ContinuousMapping(Color.WHITE,
33 ObjectMapping.NODE_MAPPING);
34 continuousMapping.setControllingAttributeName("attr3", network, false);
35
36 Interpolator numToColor = new LinearNumberToColorInterpolator();
37 continuousMapping.setInterpolator(numToColor);
38
39 Color underColor = Color.GRAY;
40 Color minColor = Color.RED;
41 Color midColor = Color.WHITE;
42 Color maxColor = Color.GREEN;
43 Color overColor = Color.BLUE;
44
45 // Create boundary conditions less than, equals, greater than
46 BoundaryRangeValues bv0 = new BoundaryRangeValues(underColor, minColor, minColor);
47 BoundaryRangeValues bv1 = new BoundaryRangeValues(midColor, midColor, midColor);
48 BoundaryRangeValues bv2 = new BoundaryRangeValues(maxColor, maxColor, overColor);
49
50 // Set the attribute point values associated with the boundary values
51 continuousMapping.addPoint(0.0, bv0);
52 continuousMapping.addPoint(1.0, bv1);
53 continuousMapping.addPoint(2.0, bv2);
54
55 Calculator nodeColorCalculator = new BasicCalculator("Example Node Color Calc",
56 continuousMapping,
57 VisualPropertyType.NODE_FILL_COLOR);
58 nodeAppCalc.setCalculator(nodeColorCalculator);
59
60
61 // Discrete Mapping - Set edge target arrow shape
62 DiscreteMapping arrowMapping = new DiscreteMapping(ArrowShape.NONE,
63 ObjectMapping.EDGE_MAPPING);
64 arrowMapping.setControllingAttributeName("interaction", network, false);
65 arrowMapping.putMapValue("pp", ArrowShape.ARROW);
66 arrowMapping.putMapValue("pd", ArrowShape.CIRCLE);
67
68 Calculator edgeArrowCalculator = new BasicCalculator("Example Edge Arrow Shape Calculator",
69 arrowMapping, VisualPropertyType.EDGE_TGTARROW_SHAPE);
70 edgeAppCalc.setCalculator(edgeArrowCalculator);
71
72
73 // Create the visual style
74 VisualStyle visualStyle = new VisualStyle(vsName, nodeAppCalc, edgeAppCalc, globalAppCalc);
75
76 return visualStyle;
77 }
78
The following snippet of code shows how to use the above createVisualStyle(network) method. After the visualStyle is created, it will appear in the comboBox of VizMapper main panel.
1 // get the network and view
2 CyNetwork network = Cytoscape.getCurrentNetwork();
3 CyNetworkView networkView = Cytoscape.getCurrentNetworkView();
4
5 // get the VisualMappingManager and CalculatorCatalog
6 VisualMappingManager manager = Cytoscape.getVisualMappingManager();
7 CalculatorCatalog catalog = manager.getCalculatorCatalog();
8
9 // check to see if a visual style with this name already exists
10 VisualStyle vs = catalog.getVisualStyle(vsName);
11 if (vs == null) {
12 // if not, create it and add it to the catalog
13 vs = createVisualStyle(network);
14 catalog.addVisualStyle(vs);
15 }
16
17 networkView.setVisualStyle(vs.getName()); // not strictly necessary
18
19 // actually apply the visual style
20 manager.setVisualStyle(vs);
21 networkView.redrawGraph(true,true);
22
Download a sample plugin, Here.
How to apply a continuous color gradient to nodes according to their degree?
To map node degree to node color, first we need to define a calculator with continuous mapping for the visual attribute NODE_FILL_COLOR. Then set the calculator to the NodeAppearanceCalculator of the visual style to be applied.
The following code snippet will set the calculator for a visualStyle.
1 // Create a node color calculator for "Degree" attribute
2 Calculator nodeColorCalculator = createCalculator(); // see below
3
4 // Set the calculator to the visualStyle
5 vs.getNodeAppearanceCalculator().setCalculator(nodeColorCalculator);
6
The following code snippet shows how to create a calculator using Degree as controlling attribute.
1 VisualPropertyType type = VisualPropertyType.NODE_FILL_COLOR;
2 final Object defaultObj = type.getDefault(Cytoscape.getVisualMappingManager().getVisualStyle());
3
4 ContinuousMapping cm = new ContinuousMapping(defaultObj, ObjectMapping.NODE_MAPPING);
5 // Set controlling Attribute
6 cm.setControllingAttributeName("Degree", Cytoscape.getCurrentNetwork(), false);
7
8 Interpolator numToColor = new LinearNumberToColorInterpolator();
9 cm.setInterpolator(numToColor);
10
11 Color underColor = Color.GRAY;
12 Color minColor = Color.RED;
13 Color midColor = Color.WHITE;
14 Color maxColor = Color.GREEN;
15 Color overColor = Color.BLUE;
16
17 BoundaryRangeValues bv0 = new BoundaryRangeValues(underColor, minColor, minColor);
18 BoundaryRangeValues bv1 = new BoundaryRangeValues(midColor, midColor, midColor);
19 BoundaryRangeValues bv2 = new BoundaryRangeValues(maxColor, maxColor, overColor);
20
21 // Set the attribute point values associated with the boundary values
22 // The points p1, p2, p3 are the values between (min~max) of the degree
23 cm.addPoint(p1, bv0); cm.addPoint(p2, bv1); cm.addPoint(p3, bv2);
24
25 // Create a calculator
26 BasicCalculator myCalculator = new BasicCalculator("My degree calcualtor", cm, VisualPropertyType.NODE_FILL_COLOR);
27
Download a sample plugin, Here.
How to load a visual properties file?
Cytoscape supported import/export of visual properties file. They can be found at File --> Import --> Vizmap Property File and File--> Export --> Vizmap Property File. To load a vizmap property file programmatically, we can reuse most of the code in the Cytoscape core class “cytoscape.actions.ImportVizmapAction”.
Cytoscape already had build-in class to handle the import of Vzmap property file, all we need is to fire a VIZMAP_LOADED event as following
1 Cytoscape.firePropertyChange(Cytoscape.VIZMAP_LOADED, null, file.getAbsolutePath());
2
After the event is fired, a dialog box will prompt the Import of Vizmap Property File.
Download a sample plugin, Here.
How to write a layout algorithm?
All Cytoscape layout algorithms are grouped together and accessible though the layout menu. To add a new layout algorithm, the new layout class should implement the interface CyLayoutAlgorithm, or extend the AbstractLayout class. The new algorithm should then register with the CyLayout manager. In this way, a new menu item, name defined in the new layout algorithm, will be available under Layout menu.
The following line shows how to register a layout algorithm with the layout manager,
1 CyLayouts.addLayout(new MyLayout(), "My Layouts");
2
One of the methods that a CyLayoutAlgorithm must implement is a getSettings() method, which returns an object of LayoutProperties that winds up under the Settings menu. Layout parameters may be supplied with the LayoutProperties. For example, the following statement defines a parameter in the LayoutProperties – name, description, data type and value.
1 layoutProperties.add(new Tunable("groupcount", "Number of random groups ",
2 Tunable.INTEGER, new Integer(2)));
3
Download a sample plugin, Here.
How to write a Group Viewer?
A class of CyGroupViewer must implement the CyGroupViewer interface and it should register with the CyGroupManager.
1 MyGroupViewer myView = new MyGroupViewer();
2 // Register with CyGroup
3 CyGroupManager.registerGroupViewer(myView);
4
Download a sample plugin, Here.
How to add components to the node view, edge view, and attribute browser context menus?
To add a component to content menu of a node view, first we need create a listener class, which implements the interface NodeContextMenuListener, then register this listener through the network view.
Here is what the listener class looks like,
1 class MyNodeContextMenuListener implements NodeContextMenuListener {
2 public void addNodeContextMenuItems(NodeView nodeView, JPopupMenu menu)
3 {
4 JMenuItem myMenuItem = new JMenuItem("MyNodeMenuItem");
5
6 myMenuItem.addActionListener(new MyNodeAction(nodeView));
7 if (menu == null) {
8 menu = new JPopupMenu();
9 }
10 menu.add(myMenuItem);
11 }
12 }
13
The method addNodeContentMenuItems() takes two arguments, NodeView and !JPopupMenu. The !JPopupMenu will be the one show up when user right-click on the NodeView on canvas. The sample code above will add a new menu item “MyNodeMenuItem” in the popupMenu. The MyNodeAction class, which implements ActionListener, will be the class performs the action after the menu item is clicked. The NodeView parameter to the listener will provide the reference to the Node via NodeView.getNode() method and take whatever action desired.
The following two lines will register the listener with the current view,
1 MyNodeContextMenuListener l = new MyNodeContextMenuListener();
2 Cytoscape.getCurrentNetworkView().addNodeContextMenuListener(l);
3
The sample works for NodeView, case for the EdgeView is very similar.
Attribute browser is a Cytoscape core plugin, it also provides interface to add new menu item to the conextMenu (since Cytoscape 2.6).
1 import browser.AttributeBrowserPlugin;
2 …
3
4 JMenuItem menuItem = new JMenuItem("MyMenuItem_browser");
5 menuItem.addActionListener(actionListener);
6 AttributeBrowserPlugin.addMenuItem(browser.DataObjectType.NODES, menuItem);
7
Note that there are three browser panels for NODE, EDGE and NETWORK, we should explicitly specify which browser a menuItem will be added. In the sample code above, a menu item is added to contextMenu of the node browser.
Download a sample plugin, Here.
How to save/restore plugin states?
All Cytoscape plugin extends CytoscapePlugin class, which implements PropertychangeListener. In order to save/restore plugin properties (globally), the plugin need to overwrite the method “onCytoscapeExit()” for property saving and some code in plugin constructor for restore.
1 /**
2 * Save global state to "tutorial21.props"
3 */
4 public void onCytoscapeExit() {}
5
6 // restore state
7 public void restoreInitState() {
8 File global_prop_file = CytoscapeInit.getConfigFile("tutorial21.props");
9 }
10
If plugin need to save states in Session, overwrite the following two methods.
1 public void saveSessionStateFiles(List<File> pFileList) {}
2 public void restoreSessionState(List<File> pStateFileList) {}
3
Download a sample plugin, Here.
How to use Cytoscape task monitor to show the progress of my job?
If a task will take a long time to finish, it is better to use Cytoscape task manager to run it in a separate thread and show its progress to the user. With the Task manager, it is possible to cancel the task, and the window won’t freeze.
Download a sample plugin, Here.
How to add new attribute functions via a Cytoscape plug-in
The easiest part is writing the actual plug-in class, which look similar to this:
1 package cytoscape.tutorial23;
2
3
4 import cytoscape.data.eqn_attribs.AttribParser;
5 import cytoscape.data.eqn_attribs.Parser;
6 import cytoscape.plugin.CytoscapePlugin;
7
8
9 public class Tutorial23 extends CytoscapePlugin {
10 public Tutorial23() {
11 final AttribParser theParser = Parser.getParser();
12 theParser.registerFunction(new IXor());
13 }
14 }
15
Here we register a new built-in function called IXor. You can also register multiple built-in functions if you prefer.
The next part is creating one class each for every new built-in. Each such class has to implement the cytoscape.data.eqn_attribs.AttribFunction interface. The easiest way to get started is to peruse the existing built-ins in .../src/cytoscape/data/eqn_attribs/builtins/ and look for a function with a similar or identical argument list. If you can't find one it may still be instructive to read through the implementation of a couple of existing functions.
The most trivial to implement methods are getName(), getFunctionSummary(), and getUsageDescription().
1 /**
2 * Used to parse the function string. This name is treated in a case-insensitive manner!
3 * @returns the name by which you must call the function when used in an attribute equation.
4 */
5 public String getName() { return "IXOR"; }
6
7 /**
8 * Used to provide help for users.
9 * @returns a description of what this function does
10 */
11 public String getFunctionSummary() { return "Returns an integer value that is the exclusive-or of 2 other integer values."; }
12
13 /**
14 * Used to provide help for users.
15 * @returns a description of how to use this function
16 */
17 public String getUsageDescription() { return "Call this with \"IXOR(integer1,integer2)\""; }
18
The next method is still simple but already touches on the one area that is somewhat complicated in attribute functions: data types and data type conversions!
1 public Class getReturnType() { return Long.class; }
2
Our example function is supposed to return an integer value. Integers in equation functions are represented as instances of class java.lang.Long. Therefore it is imperative not to return Integer.class. The user of the function can be blissfully unaware of this distinction! Just remember to always substitute Long for Integer and you should be fine.
The next method validateArgTypes() is used by the equation compiler to check the validity of the argument types in function call. Basically the compiler passes in the list of argument types for a given call. The method is then supposed to return the type of the result if the argument types are acceptable or null if the argument types are invalid.
1 public Class validateArgTypes(final Class[] argTypes) {
2 if (argTypes.length != 2 || (argTypes[0] != Long.class && argTypes[0] != Double.class)
3 || (argTypes[1] != Long.class && argTypes[1] != Double.class))
4 return null;
5
6 return Long.class;
7 }
8
Again we are faced with complexity due to types! We have modelled attribute equations after Exceltm which accepts floating point values wherever integers would be the only logical choice. To maintain this compatibility we always have to also accept arguments of type Double where Long would make the most sense. If you think that you can avoid this additional complexity for one of your own functions, you are probably mistaken because all constants in equations are treated as floating point values. If you omitted the support of arguments of type Double you would therefore preclude the passing of any constant values to your function(s).