Johan Lindström

NAME

Catalyst::View::GraphViz - GraphViz View Class

SYNOPSIS

Use the helper to create a View class

    script/myapp_create.pl view GraphViz GraphViz

This creates the MyApp::View::GraphViz class.

Build the GraphViz object

    #In some method (some View method, since
    #that's where the View code should go. See below.)
    use GraphViz;
    $graph = GraphViz->new();
    $graph->add_node("Hello", shape => 'box');
    $graph->add_node("world", shape => 'box');
    $graph->add_edge("Hello", "world");
    
    $c->stash->{graphviz}->{graph} = $graph;
    $c->stash->{graphviz}->{format} = "cmapx"; #HTML image map (default: png)
    

Forward to the View

    #Meanwhile, maybe in a private end action
    if(!$c->res->body) {
        if($c->stash->{template}) {
            $c->forward('MyApp::View::TT');
        } elsif($c->stash->{graphviz}->{graph}) {
            $c->forward('MyApp::View::GraphViz');
        } else {
            die("No output method!\n");
        }
    }

DESCRIPTION

This is the Catalyst view class for GraphViz. Your application subclass should inherit from this class.

This plugin renders the GraphViz object specified in $c->stash->{graphviz}->{graph} into the $c->stash->{graphviz}->{format} (one of e.g. png gif, or one of the other as_* methods described in the GraphViz module. PNG is the default format.

The output is stored in $c->response->output.

The normal way of using this is to render a PNG image for a request and let Catalyst serve it.

Another use of this View is to let it generate the text of a client side imagemap (using a SubRequest) which you then put into the web page currently being rendered. See below for an example.

BUILD THE GRAPHVIZ OBJECT IN A VIEW

The Catalyst::View::GraphViz takes a pre-built GraphViz object to render.

But where should this GraphViz object be constructed? Preferrably in a View class, since the GraphViz graph contains nodes with different colors, shapes, etc.

Consider how the GraphViz View relates to templating systems:

                     Templating System      GraphViz
                     -----------------      --------
  Model            | Model object(s)        Model object(s) (a graph)
  Output           | Rendered HTML          Rendered graph image/imagemap
  View             | TT/Mason/?             View::GraphViz
  View code        | Custom template file   Custom View class
  Set "look"       | $c->stash->{template}  $c->stash->{graphview}->{view}
  Set model object | varies                 $c->stash->{graphview}->{object}
  E.g. "look"      | update.thtml           MyApp::View::OddEvenGraph

So when using TT as a rendering engine, the template contains the instructions for how to display the Model object. You have many templates for displaying the same model object in different ways.

And when using GraphViz as a rendering engine, the View class contains the instructions for how to display the Model object. You have many View classes for displaying the same model object in different ways.

Here's how to create a specific View class for each type of graph.

MyApp::View::OddEvenGraph

As an example, let's create a view to render a graph of numbers, where the odd number nodes are boxes, and the even are ellipses.

Our model object is set like this somewhere (for a quick demo, just put it in the MyApp::default sub):

    $c->stash->{graphview}->{object} = {
        3 => 2,
        4 => 3,
        1 => 3,
        2 => 1,
    };

This can of course be anything that you can interpret as a graph and would like to visualize using GraphViz, not necessarily a single deep data structure like this. It could equally well be an array ref with model objects which together form a graph.

After setting up the model object, we assign the correct View class to render it.

    $c->stash->{graphview}->{view} = "MyApp::View::OddEvenGraph";
    $c->stash->{graphviz}->{format} = "cmapx";  #Optionally override the format

And, maybe in a private end action, we forward to the view if it's set, like this:

    sub end : Private {
        my ( $self, $c ) = @_;

        if(!$c->res->body) {
            if($c->stash->{template}) {
                $c->forward('MyApp::View::TT');
            } elsif(my $view = $c->stash->{graphview}->{view}) {
                $c->forward($view);
            } else {
                die("No output method!\n");
            }
        }
    }

That's what your application looks like. Now we need to create the actual View class MyApp::View::OddEvenGraph. Use the helper GraphView:

  script/myapp_create.pl view OddEvenGraph GraphView

In the class MyApp::View::OddEvenGraph you can modify the process sub to suit your needs.

    sub process {
        my ($self, $c) = @_;

        my $graph = $c->stash->{graphview}->{object} or
                die('No object specified in $c->stash->{graphview}->{object} for rendering');

        my $graphviz = GraphViz->new(node => {
            name => "oddeven",
        });

        $graphviz->add_node($_, shape => ($_ % 2) ? "box" : "ellipse") for(keys %$graph);
        while(my ($from, $to) = each %$graph) {
            $graphviz->add_edge($from, $to);
        }


        $c->stash->{graphviz}->{graph} = $graphviz;
        $c->forward('MyApp::View::GraphViz');

        return 1;
    }

As you can see, the purpose of this method is to transform the model graph object into a GraphViz object, which is then forwarded to the GraphViz View.

MAKE A SUBREQUEST TO GENERATE AN IMAGEMAP

Together with the ability to render a GraphViz image soon comes the need to generate a client-side imagemap which can be inserted in the web page showing the image.

You need a) an action that renders the GraphViz object in the cmapx format, and b) to call that action from within another action so you can assign the resulting HTML text to your stash and then put it in the template.

Render as an Imagemap

If your ordinary png action looks like this:

    sub png : Local {
        my ( $self, $c ) = @_;
        $c->stash->{graphview}->{view} = "MyApp::View::OddEvenGraph";
        $c->stash->{graphview}->{object} = ...;
    }

then your imap action should look like this:

    sub imap : Local {
        my ( $self, $c ) = @_;
        $c->stash->{graphview}->{view} = "MyApp::View::OddEvenGraph";
        $c->stash->{graphview}->{object} = ...;
        $c->stash->{graphviz}->{format} = "cmapx";
    }

Call the Imagemap Action

First make sure you have the SubRequest plugin loaded:

    use Catalyst qw/SubRequest/;  

This is how to perform the SubRequest. Let's assume these actions are in the Controller "Graph":

    $c->stash->{html_imagemap} = $c->subreq("/graph/imap");

    #Reset after subreq (until this bug is fixed: http://rt.cpan.org/NoAuth/Bug.html?id=15790 )
    $c->response->content_type("text/html");

Now you can simply output the imagemap text in the template

    [% html_imagemap %]
    <img name="graph" src="/graph/png" USEMAP="#oddeven" border=0>

Note 1: The name "oddeven" is the same as the one set in the

    GraphViz->new(name => "oddeven");

Note 1a: Unfortunately this isn't quite true. The name of the GraphViz image is always hardcoded to "test". Bug that needs to be fixed: http://rt.cpan.org/NoAuth/Bug.html?id=14882

Note 2: The nodes will not be clickable unless they have a URL property, so you need to specify that for each

    $graphviz->add_node(URL => "/graph/node/select?name=$name");

in the View class. Actually, there is a clever shortcut in GraphViz for this, so instead of specifying it for each node, you can set a default when calling

    my $graphviz = GraphViz->new(node => { URL => '/graph/node/select?name=\N' });

The \N is a placeholder for the name of each node.

METHODS

new($c)

The constructor for the GraphViz view. Sets up the template provider, and reads the application config.

process($c)

Render the GraphViz object specified in $c->stash->{graphviz}.

Output is stored in $c->response->output.

SEE ALSO

Catalyst, GraphViz

CHANGES

0.05

Docs

0.01 - 0.04

Makefile and Windows/Unix stuff

AUTHOR

Johan Lindstrom, johanl@cpan.org

CREDITS

Largely based on the TT view.

Obviosly uses Acme's GraphViz module, which in turn uses the brilliant GraphViz package (http://www.graphviz.org/).

Quick link to a useful, but obscure, doc page: http://www.graphviz.org/pub/scm/graphviz2/doc/info/shapes.html

COPYRIGHT

This program is free software, you can redistribute it and/or modify it under the same terms as Perl itself.




Hosting generously
sponsored by Bytemark