4. 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)