CGI/Perl Guide | Learning Center | Forums | Advertise | Login
Site Search: in
Add ListingModify ListingTell A FriendLink to TPASubscribeNew ListingsCool ListingsTop RatedRandom Link
Newest Reviews
  • review
  • hagen software
  • NOT GPL!
  • Hagan Software
  • Wasted Time with ...
  • poor pre-sale sup...
  • no response
  • rating the offer
  • Good Stuff
  • Good idea but use...


  •  
    Perl Archive : TLC : Programming : Perl : tk : Exploring Graphviz' dotty application in a pure Perl : Tk canvas
    Guide Search entire directory 
     

    Date Published: 2002-08-28

    The purpose of this article is to offer a simple, portable, Perl/Tk application that harnesses many of the advantages of Graphviz to a native Perl/Tk application. Dot2perl allows developers most of the features in Graphviz' dotty application in a pure Perl/Tk canvas, thereby deriving the value of Graphviz' directed graphs algorithms and small data file size.


    Primer

    Graphviz ( http://www.research.att.com/sw/tools/graphviz/ ) has been freely available for some years now and has been ported to most systems. Its ability to quickly (and efficiently) render directed graphs, based on simple initialization data, has made it a favorite among those using directed graphs. Backed by some of the most talented members of AT&T Bell Labs, Graphviz has options to do almost anything you could ever want to do with a directed graph or topological layout.


    Introduction

    Graphviz is used to visualize directed graphs and process flows. Perl modules have been built to facilitate the use of Graphviz within Perl. However, the Rubicon for Perl developers has been how to import directed graphs from the Graphviz applications dot and dotty into Perl without losing the most of rich functions present in dotty. The primary benefit of Graphviz has been its rendering of directed graphs in a rapid manner. However, some auxiliary benefits of the Graphviz package are:

    Source file size.
    Graphviz can render a directed graph from a simple ASCII text file. The small size of these source files makes the use of Graphviz ideal for organizations wanting to use directed graphs without the large files associated with large image files.

    Cross Platform.
    Graphviz is available almost all platforms Perl runs on.

    Bindings.
    Graphviz uses the dotty.lefty files to allow users to perform actions on selected nodes. These files can be modified to allow the user to ``drill-down'' on a selected node, using DBI, sockets, OLE or other methods. This adds value to the end-user by allowing Graphviz to give both the (graphical) 'big-picture' and desired detail.

    Automatic reload/updates.
    Graphviz/dotty allows the user to reload the dot file when changes occur. This allows dotty to be used as a 'real-time' application.

    Graphviz is distributed under and open source license.
    http://www.research.att.com/sw/tools/graphviz/license/

    The purpose of this article is to offer a simple, portable Perl/Tk application that harnesses many of the functions in found in dotty on a Perl/Tk canvas. This means, rather than just using Graphviz to generate an image file, we use Graphviz to generate layout data for our canvas, which we load and tag into our application. Using the tags, we construct pop-up menus to bind actions to selected items.

    Developing dot2perl was relatively simple. The documentation for dotty shows an option, -Tplain, that outputs textual data on the layout of a dot file (see Drawing graphs with dot, page 21), which can be used to place items on a Perl/Tk Canvas. Bindings and tags can then be added to mimic the dotty.lefty actions. If desired, printing can be enabled by using $canvas->postscript and Perl::Magik.

    An example will best show how Graphviz and dot2perl are used.


    Example use of Graphviz and dot2perl

    Suppose a computer science instructor wishes to illustrate the history of some operating systems. Like a family tree, this directed graph would show associations between systems over time. Children would (usually) be the result of parents. Textually, the instructor might wish to view associations in a manner similar to this:

    5th ed -> 6th ed

    Figure 1.1 parent/child relationships

    While such a pedant example is easy to understand, large amounts of data can quickly overwhelm and cause confusion, rather than clarify. This is where the benefit of Graphviz and the ability to view information as a directed graph, rather than textual data, are seen. The instructor can create a text file, using the format in Figure 1.1 and Graphviz will use that data to create a directed graph.

    Suppose the instructor creates associations between operating systems into a file status.dot.

    (Note: this is the crazy.dot file in $GRAPHVIZ/graphs/directed):

     digraph "unix" {
         graph [     fontname = "Helvetica-Oblique",
              fontsize = 36,
              label = "\n\n\n\nObject Oriented Graphs\nStephen North, 3/19/93",
              size = "6,6" ];
         node [     shape = polygon,
              sides = 4,
              distortion = "0.0",
              orientation = "0.0",
              skew = "0.0",
              color = white,
              style = filled,
              fontname = "Helvetica-Outline" ];
         "5th Edition" [sides=9, distortion="0.936354", orientation=28, skew="-0.126818", color=salmon2];
         "6th Edition" [sides=5, distortion="0.238792", orientation=11, skew="0.995935", color=deepskyblue];
         "PWB 1.0" [sides=8, distortion="0.019636", orientation=79, skew="-0.440424", color=goldenrod2];
         LSX [sides=9, distortion="-0.698271", orientation=22, skew="-0.195492", color=burlywood2];
         "1 BSD" [sides=7, distortion="0.265084", orientation=26, skew="0.403659", color=gold1];
         "Mini Unix" [distortion="0.039386", orientation=2, skew="-0.461120", color=greenyellow];
         Wollongong [sides=5, distortion="0.228564", orientation=63, skew="-0.062846", color=darkseagreen];
         :
         :
         :
         "USG 3.0" [distortion="-0.848455", orientation=44, skew="0.267152", color=bisque2];
         "Unix/TS 1.0" [distortion="0.305594", orientation=75, skew="0.070516", color=orangered];
         "TS 4.0" [sides=10, distortion="-0.641701", orientation=50, skew="-0.952502", color=yellow];
         "System V.0" [sides=9, distortion="0.021556", orientation=26, skew="-0.729938", color=darkorange1];
         "System V.2" [sides=6, distortion="0.985153", orientation=33, skew="-0.399752", color=darkolivegreen4];
         "System V.3" [sides=7, distortion="-0.687574", orientation=58, skew="-0.180116", color=lightsteelblue1];
         "5th Edition" -> "6th Edition";
         "5th Edition" -> "PWB 1.0";
         "6th Edition" -> LSX;
         "6th Edition" -> "1 BSD";
         "6th Edition" -> "Mini Unix";
         "6th Edition" -> Wollongong;
         "6th Edition" -> Interdata;
         Interdata -> "Unix/TS 3.0";
         Interdata -> "PWB 2.0";
         Interdata -> "7th Edition";
         "7th Edition" -> "8th Edition";
         "7th Edition" -> "32V";
         "7th Edition" -> V7M;
         "7th Edition" -> "Ultrix-11";
         "7th Edition" -> Xenix;
         "7th Edition" -> "UniPlus+";
         V7M -> "Ultrix-11";
         "8th Edition" -> "9th Edition";
         "1 BSD" -> "2 BSD";
         "2 BSD" -> "2.8 BSD";
         "2.8 BSD" -> "Ultrix-11";
         "2.8 BSD" -> "2.9 BSD";
         "32V" -> "3 BSD";
         "3 BSD" -> "4 BSD";
         "4 BSD" -> "4.1 BSD";
         "4.1 BSD" -> "4.2 BSD";
         "4.1 BSD" -> "2.8 BSD";
         "4.1 BSD" -> "8th Edition";
         "4.2 BSD" -> "4.3 BSD";
         "4.2 BSD" -> "Ultrix-32";
         "PWB 1.0" -> "PWB 1.2";
         "PWB 1.0" -> "USG 1.0";
         "PWB 1.2" -> "PWB 2.0";
         "USG 1.0" -> "CB Unix 1";
         "USG 1.0" -> "USG 2.0";
         "CB Unix 1" -> "CB Unix 2";
         "CB Unix 2" -> "CB Unix 3";
         "CB Unix 3" -> "Unix/TS++";
         "CB Unix 3" -> "PDP-11 Sys V";
         "USG 2.0" -> "USG 3.0";
         "USG 3.0" -> "Unix/TS 3.0";
         "PWB 2.0" -> "Unix/TS 3.0";
         "Unix/TS 1.0" -> "Unix/TS 3.0";
         "Unix/TS 3.0" -> "TS 4.0";
         "Unix/TS++" -> "TS 4.0";
         "CB Unix 3" -> "TS 4.0";
         "TS 4.0" -> "System V.0";
         "System V.0" -> "System V.2";
         "System V.2" -> "System V.3";
     }

    Figure 1.2 A dot file used by Graphviz to create directed graphs

    If the instructor used status.dot as input to the Graphviz application dotty, This would render an image on the dot canvas similar to the illustration below:



    Illustration 1.3 Graphical Output from Dot.

    A gif or postscript image file could be created in a similar fashion using the source file and dot.

    Now, suppose the instructor wished to render a similar directed graph using dot2perl. Knowing that the command dot -Tplain crazy.dot you would get the output:

     graph 0.370 16.069 16.194
     node "5th Edition"  9.625 15.875 1.894 0.628 "5th Edition" filled polygon salmon2
     node "6th Edition"  7.014 14.778 2.340 0.538 "6th Edition" filled polygon deepskyblue
     node "PWB 1.0"  11.306 14.778 1.112 0.507 "PWB 1.0" filled polygon goldenrod2
     node LSX  3.194 13.556 1.140 0.536 LSX filled polygon burlywood2
     node "1 BSD"  4.500 13.556 0.988 0.541 "1 BSD" filled polygon gold1
     node "Mini Unix"  7.958 13.556 1.556 0.500 "Mini Unix" filled polygon greenyellow
     node Wollongong  9.736 13.556 1.505 0.533 Wollongong filled polygon darkseagreen
     node Interdata  6.083 13.556 1.694 0.704 Interdata filled polygon dodgerblue1
     node "Unix/TS 3.0"  11.208 9.653 2.394 0.736 "Unix/TS 3.0" filled polygon thistle2
     node "PWB 2.0"  11.208 12.222 1.847 0.561 "PWB 2.0" filled polygon darkolivegreen3
     node "7th Edition"  4.639 12.222 1.193 0.643 "7th Edition" filled polygon chocolate
     node "8th Edition"  7.903 6.042 2.518 0.804 "8th Edition" filled polygon turquoise3
     node "32V"  4.639 10.931 1.246 0.606 "32V" filled polygon steelblue3
     node V7M  0.625 9.653 1.257 0.747 V7M filled polygon navy
         :
         :
         :
     edge "PWB 1.0" "USG 1.0" 4 11.583 14.597 11.903 14.389 12.458 14.028 12.819 13.778 solid black
    Editor's Note: Output snipped for legibility. Full output is here.

    Figure 1.4 Output from dot -Tplain status.dot

    The instructor would run dot2perl (it uses the source file status.dot by default), and it would return a Perl/Tk Canvas similar to the illustration below.

    Illustration 1.5 a simple dot file on Perl/Tk using dot2perl


    Mechanics

    Generating canvas data with dot2perl from dot -Tplain output.

    Dot2perl uses the output of dot -Tplain status.dot. It filters out lines containing node and edge to draw polygons, rectangles and lines on a Perl/Tk canvas. Tags are added as each item is created on the canvas. Here is a description of how dot2perl works:

    1. Format the source file. This action generates text similar to Figure 1.4. Dot2perl uses this data to draw the Perl/Tk Canvas:
       @cmd=`dot -Tplain status.dot`;

    2. Re-Format the data, as needed. Dot2perl eliminates all blanks in an objects name using regexp. For example, the line:
       node "5th Edition"  9.625 15.875 1.894 0.628 "5th Edition" filled polygon salmon2

      is changed to

       node "5th_Edition"  9.625 15.875 1.894 0.628 "5th Edition" filled polygon salmon2

      with the regular expressions

       chomp($item);
       $item=~s/\+*//g;
       $item=~s/\?*//g;
       $item=~s/\**//g;
       @stuff1=split(/\"/,$item);
       $namer1=$stuff1[1];
       $namer2=$namer1;
       $namer2=~s/ +/_/g;
       $item=~s/$namer1/$namer2/g;

    3. Read input line:
       node "TS_4.0"  12.069 7.319 1.486 0.752 "TS 4.0" filled polygon yellow

    4. Split line into applicable fields:
       ($node,$name1,$x1,$y1,$width,$height,$name2,$filled,$objtype,@misc) =
           split(/ +/,$item);

    5. Translate x/y coords into Perl/Tk Canvas x/y coords:
       $x1=int($x1 * 80 )  ;
           $x2=int($x1 +  ( $width * 70 )  ); 
           $y1=int($y1 * 90 ) ;
           $y2=int($y1 +  ( $height  * 70 )); 
           $x3=int(($x1+$x2)/2);
           $y3=int(($y1+$y2)/2);
           $y3=$y1 + 15;

    6. Render object (Oval or Rectangle, based on $objtype):
       if ( $objtype=~/polygon/i ) {
           $cscroll->createOval($x1,$y1,$x2,$y2,-fill=>$color,-tags=>[$name1,'box']);
       } else { 
           $cscroll->createRectangle($x1,$y1,$x2,$y2,-fill=>$color,-tags=>[$name1,'box']);
       }

    To draw a line, the process is essentially the same; the only change is the input line has two name fields, one denoting the origin node and the other noting the destination node (shown below). Both these names are loaded into tags for the line item.

     edge "CB Unix 3" "TS 4.0" 7 13.792 9.458 13.264 9.306 12.625 9.069 12.292 8.778 
        12.069 8.569 12.028 8.056 12.042 7.694 solid black

    Editor's Note: Above line wrapped for page legibility.

    Bindings

    Having established the tags, bindings are relatively simple. By default, dot2perl offers 3 bindings: mouse_in, mouse_out and a pop-up menu. The bindings reference subroutines:

     $cscroll->bind('all',"<Any-Enter>", [\&cscroll_enter]);
     $cscroll->bind('all',"<Any-Leave>", [\&cscroll_leave,\$ref_old_fill]);
     $cscroll->bind('box',,"<Button-3>",[\&display_menu,]);

    The cscroll_enter and cscroll_leave subroutines are similar to those in the widget example provided with the Perl/Tk distribution. The display_menu operates on a pop-up menu in the canvas.

    Creating the pop-up menu.

    After creating MainWindow, we create a pop-up menu with the following code:

     #--the pop up for each tagged item in canvas
     $menu=$mw->Menu(-tearoff => 0,
         -menuitems=> [ 
                    ['command' => "$boxname",-command => \&null],
                    "-",
                    ['command' =>"Stop Job",-command => \&test],
                    ['command' =>"Start Job",-command => \&test],
                    ['command' =>"Pause Job",-command => \&test],
                    ['command' =>"Resume Job",-command => \&test],
                    ['command' =>"Job Status",-command => \&test],
                    ['command' =>"Laminate Job",-command => \&test],
                    ['command' =>"Edit Job",-command => \&test]
                         ]
     );

    Having this menu, the subroutine display_menu can then extract tags and pop-up a menu over a user specified node or line:

     sub display_menu {
              @curr_tags=$cscroll->gettags('current');
              $boxname="@curr_tags";
              $boxname=~s/ +.*//g;
              $boxname="Job: $boxname";
              $menu->delete(0,0);
              $menu->insert(0,'command',-label=>"$boxname");
              $menu->Popup(-popover=>"cursor");
     }

    Zooming

    Zooming within a Perl/Tk canvas uses the $cscroll->scale(tag) routine.

    Printing

    By default, Perl/Tk canvas can print out a postscript file of the canvas. The syntax for this is:

     $cscroll->postscript(-file => "status.ps");


    Care & Feeding of dot2perl

    Running dot2perl is simple. Assuming Graphviz and Perl/Tk is installed (you can verify Perl/Tk is installed by typing widget and Graphviz is installed by typing dotty), then just

    1. Untar the package into the target directory:
       tar -xvf perl_dot2perl.tar

      (pkzip can also be used to extract the files)

    2. Change into the dot2perl bin directory (this illustration assumes it was extracted in the target directory c:\apps):
       cd c:\apps\dirgraph\bin

    3. Copy the desired dot file into the bin dir as status.dot
       copy c:\temp\g1.dot c:\apps\dirgraph\bin\status.dot

      (the Graphviz package has many directed graphs in the $GRPAHVIZ/graphs/directed directory to work with)

    4. Run the application: (download canvas1.pl)
       perl canvas1.pl

      You should have a Perl/Tk canvas similar to Illustration 1.5.


    Events and Bindings

    After starting canvas1.pl, you notice the buttons Zoom_in, Zoom_out, Print and Exit.

    Moving the mouse over nodes you notice tag data is reflected in the Label above the Button frame. This metadata is simply reading the Perl/Tk tags for the entity and updating the label above the buttons. Putting the cursor over an line or node also changes it to SeaGreen. A right mouse click renders a menu for the entity (change the display_menu subroutine in canvas1.pl to modify this menu.

    Some useful add-on subroutines for canvas1.pl (these are left to the reader as an exercise) are:

    • Print image as a GIF, using $map1->postscript() and Perl::Magick.

    • Put canvas1.pl in a while() loop and redraw the layout when status.dot changes, using the stat() to determine file mtime.

    • Change the display_menu to extract relevant data for a node using OLE, sockets, DBI, dbm or ipcs routines.


    Files and directories associated with dot2perl

    Dot2perl was designed to be simple. In accordance with this directive, the directory/file structure is simple:

    Directories

      bin        contains all source, cfg and rendered images.


    Future Work

    Extracting dot -Tplain data to create HTML IMG maps for webpages.


    Conclusions

    Dot2perl offers a simple method to use the power of Graphviz within a pure Perl/Tk application.


    Author

    David Hussman dthusma@balsasoft.com

     
     


    About The Perl ArchiveLink Validation ProcessSearch Tips
    Web Applications & Managed Hosting Powered by Gossamer Threads
    Visit our Mailing List Archives