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).
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
#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_ */
#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; }
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.
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
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
#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
#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(); }
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)
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(...)
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
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)