Using and configuring Dotty

Dotty is graph visualizer and editor provided in open source by At&T.

Table of contents

Grouping nodes when using dotty,

Configuring dotty: adding actions in menus, An hello world! example, More compact customization.

Grouping nodes when using dotty

When you group several nodes using Dotty, they are replaced with a single node. All edges coming from a node outside the grouped ones to one of the grouped ones are replaced with an arrow from the same outside node to the single node. The same operation (only symetric) is performed for outgoing edges. Arrows going from one node in the group to the others are replaced with arrows from the single node to itself.

To group nodes together using the interactive tool, you need to designate all the nodes that you want to group. To do this, you add an attribute to all the nodes involved in the operation, using the set attr operation from the node menu (the menu that pops up when clicking with the right button of the mouse on a node). The attribute name and value should be the same for all the nodes. When you have finished marking all the nodes, you select any one and you choose the option group from the node menu. The new node that is created will be named after the value of the common attribute you chose to designate the group.

Configuring dotty: adding actions in menus

Dotty is built on top of lefty, is simple language to describe application that perform display and manipulation of symbolic data. To configure dotty, one easy trick is to modify the lefty files that describe its behavior.

Instead of modifying directly the instance of dotty that everybody uses, you can copy the file of interest in one of you directories and tell lefty to use your files in preference to the standard ones, using a variable called LEFTYPATH. Obviously LEFTYPATH is used in the same manner as a PATH variable. When you set LEFTYPATH, you must be careful that the default location for lefty files must be given in that variable. Thus, if you modify a standard file from the graphviz distribution and put it in $DIR you will still need to refere to the graphviz distribution for the other files. In my case, I set LEFTYPATH to one of the following values (depending on the version of graphviz I am using):

$HOME/experiments/my-dotty-files:/usr/local/graphviz/lib

$HOME/experiments/my-dotty-files:/usr/local/graphviz/lib/lefty

An hello world! example

As an example here, I will describe how to add a simple function in the node menu (the one that pops up when clicking with the right button of the mouse on a node). The function's behavior will be to print "hello world!" in the process's standard output. The option name (in the menu) will be "hello".

First, I need to add a new function in the array that contains all the functions triggered by the node menu. This array is defined in the file dotty_ui.lefty. I copy this file in the directory $HOME/experiments/my-dotty-files and find the place where that array is defined. It looks like this:

];
dotty.protogt.actions.node = [
    "cut" = function (gt, vt, obj, data) {
        gt.cut (gt, obj, 'one', 'support', 'cut');
        dotty.clipgt.layoutgraph (dotty.clipgt);
    };
    "Cut" = function (gt, vt, obj, data) {

I add my own function, so that the file now looks like this:

];
dotty.protogt.actions.node = [
    "hello" = function (gt, vt, obj, data) {
        echo ("hello world!");
    };
    "cut" = function (gt, vt, obj, data) {
        gt.cut (gt, obj, 'one', 'support', 'cut');
        dotty.clipgt.layoutgraph (dotty.clipgt);
    };
    "Cut" = function (gt, vt, obj, data) {

Now, the only operation that remains to be done is to add this function as an extra option in the menu. The menu is also defined as an array: it is defined at a place that look like this:

dotty.protovt.normal.menus = [
    'general' = [
        0 = "undo";
 ...
        20 = "quit";
    ];
    'node' = [
        0 = "cut";
        ...
        11 = "print attr";
    ];

I simply add a new element in the 'node' array, like this:

dotty.protovt.normal.menus = [
    'general' = [
        0 = "undo";
 ...
        20 = "quit";
    ];
    'node' = [
        0 = "cut";
        ...
        11 = "print attr";
        12 = "print attr";
    ];

Now to test my customization, I simply need to set the LEFTYPATH as mentionned above and run dotty as usual.

More compact customization

The technique proposed in the previous section to customize dotty is not very satisfactory, since it requires that we copy and modify a file from the standard distribution. Here is another method, where we create a new file that contains only the modifications. The file will be called edotty.lefty. In this file, we only need to express that we add elements in two arrays. This is done with the following text:

load('dotty.lefty');
dotty.protogt.actions.node["hello"] =
  function(gt, vt, obj, data) {
    echo("hello world!");
  };
dotty.protovt.normal.menus['node'][12] = "hello";

We also add a few commands that are also inserted by the usual dotty starting script:

dotty.protogt.layoutmode = 'async';
monitorfile = dotty.monitorfile;

We can then run lefty to work on my new specification:

lefty -e "load('./edotty.lefty');dotty.simple('file');"

In this example, file represents a graph we want to see or edit with my new dotty. Taking inspiration from the dotty starting script, it is actually possible to construct a script edotty that performs the right call to lefty depending on the presence of a file argument to represent the graph to display.

Extension for fast grouping

As an application of these techniques to extend dotty, we are going to provide a new operation to group nodes, which we hope will be quicker. The main reason for this to be true is that our new technique is based on interactions using the mouse only.

The idea here is to designates a node as the target for groups, then nodes are selected one by one to be added to the group and the grouping operation is done directly. To provide this behavior we only need to add two option in the node menu. The first option is used to select the group leader, that is the node that will receive all the others. The second option is used to include a node in the group. Here is the code for these operations:

"set target" = function (gt, vt, obj, data) {
        if(obj.nid >= 0) {
            if(dotty.target) {
              gt.undrawnode(gt, gt.views, dotty.target);
              dotty.target.attr['color']='black';
              gt.unpacknodeattr(gt, dotty.target);
              gt.drawnode(gt, gt.views, dotty.target);
            }
            dotty.target = obj;
            dotty.target.attr['group'] = dotty.target.name;
            echo ('setting ', obj.name, ' as target');
            gt.undrawnode(gt, gt.views, obj);
            obj.attr['color']='blue';
            gt.unpacknodeattr(gt,obj);
            gt.drawnode(gt, gt.views, obj);
        }
    };
"merge in group" = function (gt, vt, obj, data) {
       local grouplabel, nlist;
        if(obj.nid >= 0) {
          if(dotty.target) {
            if(dotty.target.attr['grouped']) {
              grouplabel = dotty.target.attr['label'];
            } else {
              grouplabel = dotty.target.name;
            }
            echo('adding ', obj.name, ' to group ', grouplabel);
            gt.undrawnode(gt, dotty.target);
            obj.attr['group']= grouplabel;
            dotty.target = 
              gt.groupnodes(gt, [obj.nid = obj;],
                  dotty.target, dotty.target.pos, dotty.target.size,
                ['label' = grouplabel;
                 'color' = 'blue';
                 'grouped' = 1; ], 0, 1);
            gt.redrawgraph(gt, [vt.vtid = vt;]);
          }
        }
    };

Note that the group leader is also marked in a visible manner, by changing its color to blue. Maybe a change of shape would be useful too. Also, a textual trace of the grouping operation is sent to the standard output. This may be useful if one wants to keep a trace of all interactive operations.


Yves Bertot
Last modified: Tue May 16 17:18:43 MEST 2000