Table of Contents
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 :
Property algorithms :
This kind of plug-ins will only affect a specific property of any type. See Section 1, “The PropertyAlgorithm class.”.
Generic algorithms :
This kind of plug-ins can be used to modify the entire graph. In other case it is preferable to use a specific class of algorithms to implement your plug-in. See Section 2, “The Algorithm class.”.
Importation algorithms :
Plug-ins to import a specific graph. For example a plug-in that will create a graph from a file directory system. See Section 3, “Import plug-ins”.
Export algorithms :
Plug-ins to save a graph to a specific type of file. See Section 4, “Export plug-ins”.
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 name | Data member | Graph property replaced | Algorithms GUI submenu |
---|---|---|---|
BooleanAlgorithm | booleanResult | viewSelection | Selection |
ColorAlgorithm | colorResult | viewColor | Color |
DoubleAlgorithm | doubleResult | viewMetric | Measure |
Algorithm | NA | NA | General |
IntegerAlgorithm | integerResult | viewInt | Integer |
LayoutAlgorithm | layoutResult | viewLayout | Layout |
SizeAlgorithm | sizeResult | viewSize | Size |
StringAlgorithm | stringResult | viewLabel | Label |
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.
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.
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.
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”.
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.
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.
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 class PluginProgress can be used to interact with the user. Following is a list of its 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
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.
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");