Tulip QT Library

Introduction
In Tulip the plugin system is extended to interactors, views and controllers
An interactor provide a mechanisme to modify view and/or data (graph)
A view is a way to visualize graph (and others data if you want)
A controller is here to change the aspect of Tulip and to manage views
Here you have a picture represent a modified Model-View-Controller architectural pattern
Controller manage views and model (graph)
Views use model to display it
Interactors is a mini controller system who manage attached view and model

Interactors
An interactor is construct to interact with a view
In node link diagram view we have many interactors, for example : node builder interactor, selection interactor ... All these interactors have icons and this icons are visible in the toolbar
In interactor Tulip system we have two main classes : InteractorComponent and Interactor itself
Interactor is assembling a set of several interactor component

Interactor example
If you want, you can download an interactor example here
Extract archive, go in the directory, modify your PATH environment varialbe, run make and make install
PATH environment variable must contain the directory where you install you tulip. Here you have an example to modify this variable : export PATH=/home/user/install/tulip/bin:$PATH
All the code of this interactor is commented inside

Interactor Component
An interactor component is the basic building block of the interactor system
To construct an interactor component you have to build a class that inherits InteractorComponent class

InteractorComponent class
In InteractorComponent class we have 7 functions, but 4 of them are implemented on InteractorComponent. So if you want to create a new InteractorComponent, you have to implement 3 functions (4 if you count the eventFilter function)
compute : This function is call before the rendering of the scene. It can be used to add GlEntities in the scene
draw : This function is call just after the graph rendering. It can be used to add some OpenGL object directly on the OpenGL view
For example : selection interactor use this function to draw the selection rectangle
clone : This function must be implemented to return a copy of the interactor component
eventFilter : More important function ! This function is call by Qt to treat the event. You must reimplement this function and treat the event if you can

Interactor itself
You have two options to create an interactor :
Directly implement Interactor interface
Implement InteractorChainOfResponsibility class
InteractorChainOfResponsibility class is a first partial implementation of Interactor class with chain of responsibility system

InteractorChainOfResponsibility concept
In this class the interactor system is build with the chain of responsibility pattern
Here we have a chain of responsibility of InteractorComponent
You have a list of InteractorComponent. When Qt have an event, this event is passed to first element of this list, if InteractorComponent don't treat this event (function eventFilter return false) the event is passed to second element of the list ...

InteractorChainOfResponsibility class
If you want to use InteractorChainOfResponsibility system, you have some class to implement :
In you constructor :
Call InteractorChainOfResponsibility constructor with two string. First is the path to interactor icon, second is the interactor tooltip text.
Call setPriority(int) function to set the priority of the interactor in the toolbar : if priority is high ( >5 for example) interactor icon is placed at left side of the toolbar
Call setConfigurationWidgetText("Text displayed in the configuration widget of the interactor"), an other solution is to reimplement the getConfigurationWidget function who return the QWidget used to configure this interactor
Implement construct() function : in this function you have to call pushInteractorComponent(new nameOfYourInteractorComponent) to add interactor component in the chain of responsibility
Implement isCompatible(string viewName) function, if your interactor is compatible with view (with name viewName) this function must return true, else return false

Interactor class
If you want to directly use Interactor class (if you doesn't want the chain of responsibility system), you have to implement function :
setView(View *), install(QWidget *), remove(), isCompatible(string), getAction(), compute() and draw()
And you have to call setConfigurationWidgetText(string) (or reimplement getConfigurationWidget()) and setPriority(int)
Interactor function documentation is available in the Library API page

INTERACTORPLUGIN macro
After inplementation of InteractorComponent and Interactor, you have to add you interactor to plugin system :
To do that you have to call INTERACTORPLUGIN macro
INTERACTORPLUGIN(InteractorPluginClassName, "InteractorPluginName", "Authors", "Date", "Interactor plugin full name", "plugin release");

Views
A view is used to display graph data
In Tulip base you have two different views : Node link diagram view and Spreadsheet view

View example
If you want, you can download an view example here
Extract archive, go in the directory, modify your PATH environment variable, run make and make install
PATH environment variable must contain the directory where you install you tulip. Here you have an example to modify this variable : export PATH=/home/user/install/tulip/bin:$PATH
All the code of this view is commented inside

How to create a view
If you want to create a new view, you have two things to do :

Implement View interface
First you have to implement View interface
This interface can be separated into three parts :
Data part :

QWidget *construct(QWidget *parent);
// This function is call by the controller when it want to load this view
// This function must return a QWidget where we have the view
void setData(Graph *,DataSet); 
// This function is called when we load a new graph or when we create a new one
// In the DataSet you can have nothing when you create a new graph or you can have old data when you load a tlp file
void getData(Graph **, DataSet *);
// This function is called when you save your graph
// You must store the graph and the view data
voif setGraph(graph *);
// This function is called when you change the visualized graph in hierarchical view (for example)
Graph *getGraph();

Interactors part :

void setInteractors(const std::list<Interactor *> &interactors);
// This function is call by the controller to give interactors to view
std::list<Interactor *> getInteractors();
// This function must return interactors of this view (previously given interactors)
void setActiveInteractor(Interactor *interactor);
// This function is call by the controller when an interactor is activate on this view

Display part :

void draw(); 
// Call when the graph is modified or by interactors
void redraw();
// Mainly call by Qt when a menu is open in front of the view
// Data is not modified so the view is unchanged
void init();
// Call when the graph is modified and when the view need to be completely init and draw
// For example this function is call when a layout algorithm is running, in NodeLinkDiagramComponent this function call centerView() and draw()
void createPicture(const std::string &pictureName,int width=0, int height=0);
// Create a picture of this view and store it in file with pictureName

Call View plugins macro
The second thing you have to do is register your view with the ViewManager
To do that, you just have to call VIEWPLUGIN macro

VIEWPLUGIN(ViewClassName, "ViewName", "Authors", "Date", "Small View description", "View release");

An other solution : Use AbstractView or GlMainView
If you doesn't want to implement all View function, you can implement a class who inherits of AbstractView class or GlMainView class
View : is the main interface without implementation
AbstractView : provide a basic implementation of interactor system
GlMainView : provide a OpenGl view with overview

AbstractView class
AbstractView class provide a basic implementation of iteractor system, so if you want to use it you don't have to implement functions : setInteractors(...), getInteractors(...), setActiveInteractor(...) and getActiveInteractor(...)
But in your construct(..) function implementation, you have to call AbstractView::construct(...). In addition, AbstractView::contruct(...) function create a QWidget for the view, so you just have to write a code like this :

QWidget *YouViewClass::construct(QWidget *parent) {
  QWidget *widget=GlMainView::construct(parent);
  Qwidget *yourWidget=new QWidget;
  ...
  setCentralWidget(yourWidget);
  return widget;
}

In addition, AbstractView provide a system to create a context menu (the menu that appears when you click right mouse button in the view)
In this part you have 3 different functions :
buildContextMenu function : here you construct your menu, to do that you add QMenu in contextMenu parameters, for example :

contextMenu->addMenu(new QMenu("viewMenu"));

computeContextMenu function : this function is call when the user click on a menu in context menu. In this function you have to threat this action
Finaly you have specificEventFilter function : this function is call before all others function if the user move/click the mouse. You have to implement (if you want) your specific mouse mechanism on this function

GlMainView class
GlMainView class provide a OpenGl view with an overview
At default this OpenGl view display the Graph, but you can modify this default system. GlMainView use GlMainWidget system to display graph (see GlMainWidget section for more informations
In your view contruct(...) function you have to call GlMainView::construct(..)
This class implement init(...), draw(...), refresh(...) and createPicture(...) functions
In addition, this class add an hide overview button in contextMenu, but you can add your own menus in context menu (as you do with AbstractView)

GlMainView getData(..) and setData(...)
To implement your own methodes, you have two solution :
First method is ok if you want to create a view who display the graph with node/link system, this system use setData and getData of GlMainWidget
If you want you own OpenGl visualisation, you must rebuild view in setData and store data in getData

GlMainView with node/link visualisation system
For more simple explanation, we focus initially to getData(...) function
As we said above, the GlMainView use a GlMainWidget to display Graph (and other things if you want), so in getData you have to store GlMainWidget data and, if you want, you can store your own data
To do this you must have a code like this

void YourView::getData(Graph **graph,DataSet *dataSet) {
  dataSet->set<DataSet>("glMainWidgetData",mainWidget->getData());
  dataSet->set<string>("owndata","an example of own data");
  *graph=mainWidget->getGraph();
}

Is very simple
Now we focus on setData(...) function
This function is call by the controler to load (previously stored) data on your view
Like getData function, you have to load data in GlMainWidget class
You must have a code like this

void YourView::setData(Graph *graph, DataSet data){
  DataSet glMainWidgetData;
  string stringData;
  
  //We check if glMainWidgetData exist because getData can be call with empty data set (for example : if you create this view with new graph)
  if(data.exist("glMainWidgetData")
    data.get("glMainWidgetData",glMainWidgetData);
  if(data.exist("owndata")
    data.get("owndata",stringData);
    
  mainWidget->setData(graph,glMainWidgetData);
}

GlMainView with more complex visualisation
At this point you can't use getData and setData os glMainWidget
You have to completely reload you view in setData function
Why ? This is simple, setData of GlMainWidget is made to automaticaly create a node/link visualisation
So you have to manually load the data in GlMainWidget
For example if you want a view that show a simple sphere, you can create a code like this :

void YourView::setData(Graph *graph, DataSet data){
  this->graph=graph;
  
  //Create a layer with name "Main" is very important, because GlMainWidget use it
  GlLayer *mainLayer=new GlLayer("Main");
  glMainWidget->getScene()->addLayer(mainLayer);
  
  GlSphere *sphere;
  if(data.exist("spherePosition"){
    Coord position=data.get("spherePosition",position);
    sphere=new GlSphere(position,10.);
  }else{
    sphere=new GlSphere(Coord(0,0,0),10.);
  }
  
  mainLayer->addGlEntity("Sphere",sphere);
}

And if you want to store data of your view, you can do this :

void YourView::getData(Graph **graph,DataSet *data){
  *graph=this->graph;
  data->set<Coord>("spherePosition",spherePosition);
}

Overview system
At default, if you create your view, the overview will display anything because you have to give some informations to overview
You just have to call setObservedView(GlMainWidget *widget,GlSimpleEntity *entity) function
In the overview, you can display only one element (but this element can be a GlComposite) and this element must be store in entity parameter
So if you have you view which inherits of GlMainView class and you want to display the graph on the overview (graph is display at default in GlMainView), you have to enter a line like this in your setData(...) function

overviewWidget->setObservedView(mainWidget,mainWidget->getScene()->getGlGraphComposite());

Controllers
As the majority of features in eclipse, the main window's layout is controlled by plugins. The base layout is set-up by the MainController class which inherits from the Controller abstract object.
Whenever a graph is loaded into Tulip, a new controller is chosen to display it (which means that there is as many controller objects as graphs loaded into Tulip). By default, only MainController is present and is always chosen to display new graphs. However, if several controllers are available, the user will be able to chose between them when a new graph will be loaded.
First, we will create a very basic controller which do practically nothing. We'll then use it as a strat to develop a ControllerPluginExample (whose features are described below).

Empty Controller
The first step to create a controller is to build the skeleton that will be the common base between all the controller plugins you'll write.
We are going to write a controller that does practically nothing (we'll call it VoidController) and use it as a base to develop our BasicExampleController.
The process is pretty similar to other plugins. First, we'll make a class that inherits from the Controller interface, overriding some methods, then we'll declare our new class as a controller plugin using the CONTROLLERPLUGIN

VoidController.h

#ifndef VOIDCONTROLLER_H_
#define VOIDCONTROLLER_H_

#include <tulip/Controller.h>
#include <tulip/DataSet.h>

class VoidController: public tlp::Controller {
public:
    VoidController();
    virtual ~VoidController();

    virtual void attachMainWindow(tlp::MainWindowFacade facade);
    virtual void setData(tlp::Graph *graph=0,
                         tlp::DataSet dataSet=tlp::DataSet());
    virtual void getData(tlp::Graph **graph, tlp::DataSet *data);
    virtual tlp::Graph *getGraph();

private:
    tlp::Graph *graph;
};


#endif /* VOIDCONTROLLER_H_ */

VoidController.cpp

#include "VoidController.h"

using namespace tlp;
using namespace std;

CONTROLLERPLUGIN(VoidController, "VoidController", "Author","12/02/2009","Tutorial void controller", "1.0");

VoidController::VoidController() {
}

VoidController::~VoidController() {
}

void VoidController::attachMainWindow(tlp::MainWindowFacade facade) {
	Controller::attachMainWindow(facade);
}

void VoidController::setData(Graph *graph, DataSet dataSet) {
	this->graph = graph;
}

void VoidController::getData(Graph **graph, DataSet *data) {
	*graph = this->graph;
}

Graph *VoidController::getGraph() {
	return this->graph;
}

Description
The code itself is pretty self-explanatory but we'll review some of its elements.
First, we can recognize familiar methods: setData, getData and getGraph aim at the same thing as their counterparts in other plugins. Each controller is assosiated to one graph (passed via setData) and can store some restoration infos via getData and getGraph methods.
The attachMainWindow is used to build the controller's GUI. A basic main window layout is given (the facade parameter). It contains the ``File'', ``Windows'' and ``Help'' menus and the ``Open'', ``Save'' and ``Print'' toolbar buttons. You can tune-up this GUI by adding whatever elements in this tulip window.
Controllers are tabbed elements: because you have one controller per graph, if multiple controllers are simultaneously loaded into Tulip, the user we'll be able to choose between them to see every new opened graph.

Controller example
If you want, you can download an controller example here
Extract archive, go in the directory, modify your PATH environment varialbe, run make and make install
PATH environment variable must contain the directory where you install you tulip. Here you have an example to modify this variable : export PATH=/home/user/install/tulip/bin:$PATH
All the code of this controller is commented inside

ControllerPluginExample
Now we will create a very basic controller. This controller will be create a Node Link Driagram View in full window mode, and you will have interactors to modify the graph

ControllerPluginExample.h

#ifndef CONTROLLERPLUGINEXAMPLE_H
#define CONTROLLERPLUGINEXAMPLE_H

#include <tulip/ControllerViewsManager.h>
#include <tulip/DataSet.h>
#include <tulip/Graph.h>
#include <tulip/AbstractProperty.h>
#include <tulip/Observable.h>

// For this example we construct an simple controller who display a simple node link diagram view
// This Controller use implementation of ControllerViewsManager
// And observe graph and properties of graph
class ControllerPluginExample: public tlp::ControllerViewsManager, public tlp::Observer {
public:

    virtual ~ControllerPluginExample();

    // This function is call when tulip want to open it
    virtual void attachMainWindow(tlp::MainWindowFacade facade);

    // This function is call when tulip want to attach data on this controller 
    virtual void setData(tlp::Graph *graph=0,tlp::DataSet dataSet=tlp::DataSet());

    // This function is call when tulip want to save this controller
    virtual void getData(tlp::Graph **graph, tlp::DataSet *data);

    // Return the current graph of this controller
    virtual tlp::Graph *getGraph();

    // This function is call by Observable 
    // In setData, we add this controller to graph observer and properties observer
    // So this function is call when the graph is modified or when a property is modified
    virtual void update( std::set< tlp::Observable * >::iterator begin, std::set< tlp::Observable * >::iterator end);

    // This function is need by Oberver class
    virtual void observableDestroyed(tlp::Observable*){}

private:
    tlp::Graph *graph;
    tlp::View *nodeLinkView;
};

#endif

ControllerPluginExample.cpp

#include "ControllerPluginExample.h"

#include <tulip/View.h>

using namespace std;
using namespace tlp;


// VIEWPLUGIN(ClassName, "ControllerName", "Authors", "date", "Long controller plugin name", "plugin_release");
CONTROLLERPLUGIN(ControllerPluginExample, "ControllerPluginExample", "Author","12/02/2009","Controller Plugin Example", "1.0");

ControllerPluginExample::~ControllerPluginExample() {
	// when we delete this controller, we remove it of observer
	if(graph){
		Iterator<PropertyInterface*> *it = graph->getObjectProperties();
    	while (it->hasNext()) {
      	  PropertyInterface* tmp = it->next();
      	  tmp->removeObserver(this);
    	} delete it;

	graph->removeObserver(this);
	}
}

void ControllerPluginExample::attachMainWindow(tlp::MainWindowFacade facade) {
	// Call the attachMainWindow of ControllerViewsManager
	ControllerViewsManager::attachMainWindow(facade);
}

void ControllerPluginExample::setData(Graph *graph, DataSet dataSet) {
	// When we setData, we create a Node link diagram view
	nodeLinkView=ControllerViewsManager::createView("Node Link Diagram view",graph,dataSet,true,QRect(0,0,100,100),true);
	this->graph = graph;

	// We set observer to observe properties and graph
	Iterator<PropertyInterface*> *it = graph->getObjectProperties();
    	while (it->hasNext()) {
      	  PropertyInterface* tmp = it->next();
      	  tmp->addObserver(this);
    	} delete it;

	graph->addObserver(this);
}

void ControllerPluginExample::getData(Graph **graph, DataSet *data) {
	*graph = this->graph;
}

Graph *ControllerPluginExample::getGraph() {
	return this->graph;
}

void ControllerPluginExample::update ( std::set< tlp::Observable * >::iterator begin, std::set< tlp::Observable * >::iterator end) {
	// When graph or property is modified, we draw the view
	nodeLinkView->draw();
}

Description
In this example, we have two important things
First we have the ControllerViewsManager class
This class is a tools class to create and manage views. In this example, we just use it to simplify creation of the view and activation of this view (and interactor activation). If we had directly created the view, without ControllerViewsManager, the interactor tool bar was empty
The second important things is the observer system
In Tulip graph and properties can be observed, to do this you just have to create a class that inherits of Observer and you have to connect observables (graph and properties) to this observer with addObserver(...) function
But you can inherit of two others class : GraphObserver and PropertyObserver. GraphObserver is use when you want to know when a node is add/delete for example (see ObservableGraph class for more information). And PropertyObserver is use if you want to know when a property is modified (see ObservableProperty class for more information)

ControllerAlgorithmTools class
This class is a set of static function for the application of algorithm and for the test of structural properties of the graph
All these functions can be separated into three groups :
Algorithm application functions : in this group you have getPluginParameters(...), applyAlgorithm(...), changeProperty(...), changeString(...), changeBoolean(...), changeMetric(...), changeLayout(...), changeInt(...), changeColors(...) and changeSizes(...) functions
Structural test functions : isAcyclic(...), isSimple(...), isConnected(...), isBiconnected(...), isTriconnected(...), isTree(...), isFreeTree(...), isPlanar(...) and isOuterPlanar(...)
Structural modification functions : makeAcyclic(...), makeSimple(...), makeConnected(...), makeBiconnected(..) and makeDirected(...)

ControllerViewsTools class
In this class you have a set of static function to create and use views and iteractors
You have five functions :
createView(..) to create a view
createMainView(..) to create a node link diagram view
installInteractors(...) to install interactors of a view in tool bar
changeInteractor(...) to activate interactor when we click on its icon
getNoInteractorConfigurationWidget() to have an empty configuration widget for interactor

ControllerViewsManager class
This class provide a basic system to manage a multi views system
To use it, you have to create a view that inherit of this class and call ControllerViewsManager::attachMainWindow(...) in your class attachMainWindow function
Functions of ControllerViewsManager can be separate in two big group :
Setter and accessor :
getCurrentGraph() and getCurrentView() : to get current active view and graph associated with this view
getViewsNumber() : to get number of view
getGraphOfView(...), setGraphOfView(...), getViewOfWidget(...), setViewOfWidget(...), getWidgetOfView(...), getNameOfView(...) and setNameOfView(...) : to get/set association
Tool funstions :
createView(...) : to create a view
installInteractors(...) : to install interactors for a view (normaly you don't have to call manualy this function, she is atoucall when view is activated)
updateViewsOfGraph(...) and updateViewsOfSubGraphs(...) to update views
changeGraphOfViews(...) : just see name of function ;)
drawViews(...) : to draw all views
saveViewsGraphsHierarchies() and checkViewsGraphsHierarchy() : to save hierarchy of graph of views and to check if hierarchy is good (after subgraph removing for example)