Chapter 6. Plug-ins development

Table of Contents

1. The PropertyAlgorithm class.
Overview of the class
Parameters :
The PluginProgress class.
Example of a plugin skeleton
2. The Algorithm class.
Public members
Protected members
3. Import plug-ins
Public members
Protected members
Skeleton an ImportModule derived class
4. Export plug-ins
Public members
Protected members
Skeleton of an ExportModule derived class
5. Compilation ( Makefiles )

Tulip has been built to be easily extensible. Therefore a mechanism of plug-ins has been set up. It enables to directly add new functionalities into the Tulip kernel. One must keeps in mind that a plug-in have access to all the parts of Tulip. Thus, one must write plug-ins very carefully to prevent memory leak and also errors. A bug in plug-in can result in a "core dump" in the software that uses it. To enable the use of plug-ins, a program must call the initialization functions of the plug-ins. This function loads dynamically all the plug-ins and register them into a factory that will enable to directly access to it.

To develop a plug-in, you need to create a new class that will inherits from a specific algorithm class. Algorithms classes are separated in 4 different types :

Basic classes :

1. The PropertyAlgorithm class.

The PropertyAlgorithm class, is the class from which inherits different types of algorithms such as the BooleanAlgorithm class or the LayoutAlgorithm class (see picture below). This class is important in the way that every specific algorithm that you will develop will have to inherit from one of those classes. For example, if you write a plug-in to update the graph layout, your new class will have to inherit from the LayoutAlgorithm class which inherits from this PropertyAlgorithm class.

Each one of the 8 classes presented above has a public member, TypeNameProperty* typeNameResult, which is the data member that which have to be updated by your plug-in. After a successful run tulip will automatically copy this data member into the corresponding property of the graph. Following is a table showing the data member, graph property and 'Algorithms' GUI submenu corresponding to each subclass of Algorithm :

Class nameData memberGraph property replacedAlgorithms GUI submenu
BooleanAlgorithmbooleanResultviewSelectionSelection
ColorAlgorithmcolorResultviewColorColor
DoubleAlgorithmdoubleResultviewMetricMeasure
AlgorithmNANAGeneral
IntegerAlgorithmintegerResultviewIntInteger
LayoutAlgorithmlayoutResultviewLayoutLayout
SizeAlgorithmsizeResultviewSizeSize
StringAlgorithmstringResultviewLabelLabel

Note that at anytime, if the user clicks on the "cancel" button ( see the section called “The PluginProgress class.” for more details ), none of your algorithm's actions will changes the graph since the variable typeNameResult is not copied in the corresponding property.

Overview of the class

A quick overview of the functions and data members of the class PropertyAlgorithm is needed in order to have a generic understanding of its 8 derived classes.

Public members

Following is a list of all public members :

  • PropertyAlgorithm (const PropertyContext& context) :

    The constructor is the right place to declare the parameters needed by the algorithm.

    addParameter<DoubleProperty>("metric", paramHelp[0], 0, false);

    And to declare the algorithm dependencies.

    addDependency<Algorithm>("Quotient Clustering", "1.0");

  • ~PropertyAlgorithm () : Destructor of the class.

  • bool run () :

    This is the main method :

    - It will be called out if the pre-condition method (bool check (..)) returned true.

    - It is the starting point of your algorithm.

    The returned value must be true if your algorithm succeeded.

  • bool check (std::string& errMsg) :

    This method can be used to check what you need about topological properties of the graph, metric properties on graph elements or anything else.

Protected members

Following is a list of all protected members :

  • Graph* graph :

    This graph is the one given in parameters, the one on which the algorithm will be applied.

  • PluginProgress* pluginProgress :

    This instance of the class PluginProgress can be used to have an interaction between the user and our algorithm. See the next section for more details.

  • DataSet* dataSet :

    This member contains all the parameters needed to run the algorithm. The class DataSet is a container which allows insertion of values of different types. The inserted data must have a copy-constructor well done. See the section called DataSet for more details.

The methods of the TypeNameAlgorithm class, will be redefined in your plug-in as shown in the section called “Example of a plugin skeleton”.

Parameters :

Your algorithm may need some parameters, for example a boolean or a property name, that must be filled in by the user just before being launched. In this section, we will look at the methods and techniques to do so.

Adding parameters to an algorithm

The class PropertyAlgorithm inherits from a class called WithParameters that has a member function named template<typename Type> void addParameter (const char *name, const char *inHelp=0, const char *inDefValue=0, bool isMandatory=true) which is capable of adding a parameter. This method has to be called in the constructor of your class.

Following is a description of its parameters :

  • name : Name of the new parameter.

  • inHelp : This parameter can be used to add a documentation to the parameter (See example below).

  • inDefValue : Default value.

  • isMandatory : If false, the user must give a value.

On the following example, we declare a character buffer that will contain the documentation of our parameters.

 
namespace {
  const char * paramHelp[] = {
    // property
    HTML_HELP_OPEN() \
    HTML_HELP_DEF( "type", "DoubleProperty" ) \
    HTML_HELP_BODY() \
    "This metric is used to affect scalar values to graph items." \
    "The meaning of theses values depends of the choosen color model." \
    HTML_HELP_CLOSE(),
    // colormodel
    HTML_HELP_OPEN() \
    HTML_HELP_DEF( "type", "int" ) \
    HTML_HELP_DEF( "values", "[0,1]" ) \
    HTML_HELP_DEF( "default", "0" ) \
    HTML_HELP_BODY() \
    "This value defines the type of color interpolation. Following values are valid :" \
    "<ul><li>0: HSV interpolation ;</li><li>1: RGB interpolation</li></ul>" \
    HTML_HELP_CLOSE(),
    // color1
    HTML_HELP_OPEN() \
    HTML_HELP_DEF( "type", "Color" ) \
    HTML_HELP_DEF( "values", "[0,255]^4" ) \
    HTML_HELP_DEF( "default", "red" ) \
    HTML_HELP_BODY() \
    "This is the start color used in the interpolation process." \
    HTML_HELP_CLOSE(),
    // color2
    HTML_HELP_OPEN() \
    HTML_HELP_DEF( "type", "Color" ) \
    HTML_HELP_DEF( "values", "[0,255]^4" ) \
    HTML_HELP_DEF( "default", "green" ) \
    HTML_HELP_BODY() \
    "This is the end color used in the interpolation process." \
    HTML_HELP_CLOSE(),
    // Mapping type
    HTML_HELP_OPEN() \
    HTML_HELP_DEF( "type", "Boolean" ) \
    HTML_HELP_DEF( "values", "true / false" ) \
    HTML_HELP_DEF( "default", "true" ) \
    HTML_HELP_BODY() \
    "This value defines the type of mapping. Following values are valid :" \
    "<ul><li>true : linear mapping</li><li>false: uniform quantification</li></ul>" \
    HTML_HELP_CLOSE(),
  };
}
    	    		

Then, we can add the parameters in the constructor by writing the following lines:

 
    addParameter<DoubleProperty>("property",paramHelp[0],"viewMetric");
    addParameter<int>("colormodel",paramHelp[1],"1");
    addParameter<bool>("type",paramHelp[4],"true");
    addParameter<Color>("color1",paramHelp[2],"(255,255,0,128)");
    addParameter<Color>("color2",paramHelp[3],"(0,0,255,228)");
    	    		

The picture below is the result of the sample of code above.

Accessing a parameter

The class PropertyAlgorithm has a protected member called dataSet that contains all the parameters value.The DataSet class implements a container which allows insertion of values of different types and implements the following methods :

  • template<typename T> bool get (const std::string& name, T value) const :

    Returns a copy of the value of the variable with name name. If the variable name doesn't exist return false else true.

  • template<typename T> bool getAndFree (const std::string& name, T value) :

    Returns a copy of the value of the variable with name name. If the variable name doesn't exist return false else true. The data is removed after the call.

  • template<typename T> void set (const std::string& name, const T value) :

    Set the value of the variable name.

  • bool exist (const std::string& name) const :

    Returns true if name exists else false.

  • Iterator<std::pair<std::string, DataType> >* getValues () const :

    Returns an iterator on all values

Has you could have guess, the one important to access a parameter is get() which allows to access to a specific parameter. Following is an example of its use :

 
  DoubleProperty* metricS;
  int colorModel;
  Color color1;
  Color color2;
  bool mappingType = true;
  
  if ( dataSet!=0 ) {
    dataSet->get("property", metricS);
    dataSet->get("colormodel", colorModel);
    dataSet->get("color1", color1);
    dataSet->get("color2", color2);
    dataSet->get("type", mappingType);
  }
    	    	

The PluginProgress class.

The class PluginProgress can be used to interact with the user. Following is a list of its members

Public members

Following is a list of all Public members :

  • ProgressState progress (int step, int max_step) :

    This method can be used to know the global progress of or algorithm, the number of steps accomplished.

  • void showPreview (bool) :

    Enables to specify if the preview check box has to be visible or not.

  • bool isPreviewMode () :

    Enables to know if the user has checked the preview box.

  • ProgressState state () const :

    Indicates the state of the 'Cancel', 'Stop' buttons of the dialog

  • void setError (std::string error) :

    Shows an error message to the user

  • void setComment (std::string msg) :

    Shows a comment message to the user

PluginProgress example

In following small example, we will iterate over all nodes and notify the user of the progression.

 
  unsigned int i=0;
  unsigned int nbNodes = graph->numberOfNodes ();
  
  const unsigned int STEP = 10;
  
  node n; 
  forEach(n, graph->getInEdges(n))
  {
    ...
    ... // Do what you want
    ...
    if(i%STEP==0)
    {
       pluginProgress->progress(i, nbNodes); //Says to the user that the algorithm has progressed.
        
       //exit if the user has pressed on Cancel or Stop
       if(pluginProgress->state() != TLP_CONTINUE)
       {
          returnForEach pluginProgress->state()!=TLP_CANCEL;
       }
    }    
    i++;
        	
  }
    	    	

Before exiting, we check if the user pressed stop or cancel. If he pressed "cancel", the graph will not be modified. If he pressed "stop", all values computed till now will be saved to the graph.

Example of a plugin skeleton

Following is an example of a dummy color algorithm ( you can find more example in the tulip tarball ):

 
#include <tulip/TulipPlugin.h>
#include <string>

using namespace std;
using namespace tlp;

/** Algorithm documentation */
// MyColorAlgorithm is just an example
/*@{*/


class MyColorAlgorithm:public ColorAlgorithm { 
public:

  // The constructor below has to be defined,
  // it is the right place to declare the parameters
  // needed by the algorithm,
  // addParameter<DoubleProperty>("metric", paramHelp[0], 0, false);
  // and declare the algorithm dependencies too.
  // addDependency<Algorithm>("Quotient Clustering", "1.0");
  MyColorAlgorithm(PropertyContext& context):ColorAlgorithm(context) {
  }

  // Define the destructor only if needed 
  // ~MyColorAlgorithm() {
  // }

  // Define the check method only if needed.
  // It can be used to check topological properties of the graph,
  // metric properties on graph elements or anything else you need.
  // bool check(string& errorMsg) {
  //   errorMsg="";
  //   return true;
  // }

  // The run method is the main method : 
  //     - It will be called out if the pre-condition method (bool check (..)) returned true.
  //     - It is the starting point of your algorithm.
  // The returned value must be true if your algorithm succeeded.
  bool run() {
    return true;
  }
};
/*@}*/

// This line is very important because it's the only way to register your algorithm in tulip.
// It automatically builds the plugin object that will embed the algorithm.
COLORPLUGIN(MyColorAlgorithm, "My Color Algorithm", "Authors", "07/07/07", "Comments", "1.0")
// If you want to present your algorithm in a dedicated submenu of the Tulip GUI,
// use the declaration below where the last parameter specified the name of submenu.
// COLORPLUGINOFGROUP(MyColorAlgorithm, "My Color Algorithm", "Authors", "07/07/07", "Comments", "1.0", "My algorithms");