GraphViz2(3pm)
πΈ NAME πΈ
GraphViz2 β A wrapper for AT&T's Graphviz. Bestie, this is the GraphViz2(3pm) manual page, and it's literally your Perl glow-up for making graphs. π
β¨ Synopsis β¨
π Sample output π
See https://graphviz-perl.github.io/. It's giving visual masterpiece, no cap. β¨
π» Perl code π»
Typical Usage
use strict;
use warnings;
use File::Spec;
use GraphViz2;
use Log::Handler;
my $logger = Log::Handler->new;
$logger->add(screen => {
maxlevel => 'debug', message_layout => '%m', minlevel => 'error'
});
my $graph = GraphViz2->new(
edge => {color => 'grey'},
global => {directed => 1},
graph => {label => 'Adult', rankdir => 'TB'},
logger => $logger,
node => {shape => 'oval'},
);
$graph->add_node(name => 'Carnegie', shape => 'circle');
$graph->add_node(name => 'Murrumbeena', shape => 'box', color => 'green');
$graph->add_node(name => 'Oakleigh', color => 'blue');
$graph->add_edge(from => 'Murrumbeena', to => 'Carnegie', arrowsize => 2);
$graph->add_edge(from => 'Murrumbeena', to => 'Oakleigh', color => 'brown');
$graph->push_subgraph(
name => 'cluster_1',
graph => {label => 'Child'},
node => {color => 'magenta', shape => 'diamond'},
);
$graph->add_node(name => 'Chadstone', shape => 'hexagon');
$graph->add_node(name => 'Waverley', color => 'orange');
$graph->add_edge(from => 'Chadstone', to => 'Waverley');
$graph->pop_subgraph;
$graph->default_node(color => 'cyan');
$graph->add_node(name => 'Malvern');
$graph->add_node(name => 'Prahran', shape => 'trapezium');
$graph->add_edge(from => 'Malvern', to => 'Prahran');
$graph->add_edge(from => 'Malvern', to => 'Murrumbeena');
my $format = shift || 'svg';
my $output_file = shift || File::Spec->catfile('html', "sub.graph.$format");
$graph->run(format => $format, output_file => $output_file);
π Description π
π¦ Overview π¦
This module provides a Perl interface to the amazing Graphviz http://www.graphviz.org/, an open source graph visualization tool from AT&T. Iconic. π
It is called GraphViz2 so that pre-existing code using (the Perl module) GraphViz continues to work. Periodt.
To avoid confusion, when I use GraphViz2 (note the capital V), I'm referring to this Perl module, and when I use Graphviz http://www.graphviz.org/ (lower-case v) I'm referring to the underlying tool (which is in fact a set of programs). Iykyk.
Version 1.00 of GraphViz2 is a complete re-write, by Ron Savage, of GraphViz V 2, which was written by Leon Brocard. The point of the re-write is to provide access to all the latest options available to users of Graphviz http://www.graphviz.org/. Slay.
GraphViz2 V 1 is not backwards compatible with GraphViz V 2, despite the considerable similarity. It was not possible to maintain compatibility while extending support to all the latest features of Graphviz http://www.graphviz.org/. So don't just swap them and expect no drama. π
To ensure GraphViz2 is a light-weight module, Moo has been used to provide getters and setters, rather than Moose. Lowkey genius.
As of V 2.43, "GraphViz2" supports image maps, both client and server side. Wait, that's a whole feature. π«Ά
See "Image Maps" below. π
π What is a Graph? π
An undirected graph is a collection of nodes optionally linked together with edges. Simple as.
A directed graph is the same, except that the edges have a direction, normally indicated by an arrow head. Main character energy. β
A quick inspection of Graphviz http://www.graphviz.org/'s gallery http://www.graphviz.org/gallery/ will show better than words just how good Graphviz http://www.graphviz.org/ is, and will reinforce the point that humans are very visual creatures. Fr fr, go look. π
π οΈ Installation π οΈ
Of course you need to install AT&T's Graphviz before using this module. See http://www.graphviz.org/download/. Go get it, bestie. π«Ά
πΈ Constructor and Initialization π
Calling new()
new() gets called like this, bestie:
my($obj) = GraphViz2 -> new(k1 => v1, k2 => v2, ...)
It hands you back a brand-new object of type GraphViz2. β¨
Here are all the key-value pairs you can pass in the parameter list:
edge=> $hashref β points at a hashref that sets the default attributes for edges. So like, whatever keys and values you shove in there can be anything Graphviz supports (http://www.graphviz.org/). Default is{}. Optional, queen. πglobal=> $hashref β points at a hashref that sets attributes for the output stream. Also optional. The valid keys inside this one are:combine_node_and_portβ new in 2.58. It defaults to true, but eventually (currently planned for May 2021) it'll default to false. When it's true,add_nodeandadd_edgeonly escape some characters in the label and names, and specifically thefromandtoparameters on edges will combine the node name and port into one single string with a:in the middle (except for special treatment of double-colons, iykyk).
When the option is false, you can give nodes literally any name you want, and edges can be created between them. Want ports? Pass the extra parameter tailport or headport. Want a compass point on top of that? Pass array-refs with two values for those parameters. Also, add_node's treatment of labels is way more DWIM, with { and friends being transparently quoted. π¦
directed=> $Boolean β this one changes what actually lands in the output stream.directed => 1outputs'digraph name {...}', whiledirected => 0outputs'graph name {...}'. At the Perl level, directed graphs have edges with arrow heads like'->', and undirected graphs have unadorned edges like'--'. Default is 0. Optional. πdriver=> $program_name β tells it which external program to run to process the output stream. The default is to use File::Which'swhich()method to find the'dot'program. Optional.format=> $string β what type of output file to create. Default is'svg'. Output formats shaped like'png:gd'and so on are also supported, but only the component before the first:actually gets validated by GraphViz2. Optional.label=> $string β what an edge looks like:'->'for directed graphs and'--'for undirected graphs. You wouldn't normally need this one, ngl. Default is'->'if directed is 1, and'--'if directed is 0. Optional.name=> $string β affects the content of the output stream.name => 'G666'outputs'digraph G666 {...}'. Default is'Perl':-). Optional.record_shape=>/^(?:M?record)$/β affects the shape of records. The value has to be'Mrecord'or'record'. Mrecords have nice rounded corners π whereas plain old records have square corners. Default is'Mrecord'. See the Record shapes docs at http://www.graphviz.org/doc/info/shapes.html#record for details. Optional.strict=> $Boolean β affects the content of the output stream.strict => 1outputs'strict digraph name {...}', whilestrict => 0outputs'digraph name {...}'. Default is 0. Optional.timeout=> $integer β how long to wait for the external program before exiting with an error. Default is 10 (seconds). Optional.
graph=> $hashref β points at a hashref that sets default attributes for graphs. Allowable keys and values inside are anything Graphviz supports (http://www.graphviz.org/). Default is{}. Optional.logger=> $logger_object β gives you a logger object so$logger_object -> $level($message)can be called at certain times. Any object withdebuganderrormethods will do, since those are the only two levels this module ever emits. One option is a Log::Handler object. Grab and update the value with thelogger()method.
By default (aka with no logger object at all), GraphViz2 prints warning and debug messages to STDOUT, and dies upon errors. But if you supply a log object, you can capture those events instead. And not just that β you can change the behaviour of your log object any time by calling logger($logger_object). See also the verbose option, which can interact with the logger option. Optional. π
node=> $hashref β points at a hashref that sets default attributes for nodes. Allowable keys and values inside are anything Graphviz supports (http://www.graphviz.org/). Default is{}. Optional.subgraph=> $hashref β points at a hashref that sets attributes for all subgraphs, unless overridden for specific subgraphs in a call shaped likepush_subgraph(subgraph => {$attribute => $string}). The valid keys inside this hashref are:rank=> $string β affects the content of all subgraphs, unless overridden later. A typical usage would benew(subgraph => {rank => 'same'})so that all nodes mentioned within each subgraph are constrained to be horizontally aligned. Seescripts/rank.sub.graph.1.plfor sample code. Possible values for $string are:max,min,same,sinkandsource. See the Graphviz'rank'docs at http://www.graphviz.org/doc/info/attrs.html#d:rank for details.
Default is {}. Optional.
verbose=> $Boolean β controls how much output you get when a logger is not specified. Setting verbose to 0 means print nothing. Setting verbose to 1 means print the log level and the message toSTDOUT, when a logger is not specified. Grab and update the value with theverbose()method. Default is 0. See also theloggeroption, which can interact with theverboseoption. Optional.
Validating Parameters
Okay so the secondary keys (the ones hanging under the primary keys 'edge|graph|node') get checked against lists of valid attributes. Those lists live at the end of this module, after the __DATA__ token, and get made available using Data::Section::Simple. π§
The effect of this mechanism is that Graphviz (http://www.graphviz.org/) options end up hard-coded in the source code of GraphViz2. That's just how it is.
That said, these lists are implemented differently from the way it was done in V 2. V 2 ships with a set of scripts, scripts/extract.*.pl, which retrieve pages from the Graphviz (http://www.graphviz.org/) web site and extract the current lists of valid attributes. Those then get copied manually into the source code of GraphViz2 β meaning any time those lists change on the Graphviz (http://www.graphviz.org/) web site, it's a trivial matter to update the lists stored inside this module. π
See "Scripts Shipped with this Module" in GraphViz2.
Alternate constructor and object method
from_graph
my $gv = GraphViz2->from_graph($g);
# alternatively
my $gv = GraphViz2->new;
$gv->from_graph($g);
# for handy debugging of arbitrary graphs:
GraphViz2->from_graph($g)->run(format => 'svg', output_file => 'output.svg');
Takes a Graph object, and this module will figure out various defaults from it β including whether it's directed or not. π«Ά
It'll also use any node-, edge-, and graph-level attributes named graphviz as a hash-ref for setting attributes on the corresponding entities in the constructed GraphViz2 object. These will override the figured-out defaults we just mentioned.
For a "multivertexed" graph, it'll only create one node per vertex, but it will search all the multi-IDs for a graphviz attribute, taking the first one it finds (sorted alphabetically).
For a "multiedged" graph, it'll create one edge per multi-edge.
It will only set the global attribute if called as a constructor. That gets dropped from any passed-in graph-level graphviz attribute when it's called as an object method.
A special graph-level attribute (under graphviz) called groups gets even more special meaning: it's an array-ref of hash-refs. Those hash-refs have keys, and those keys are used to create subgraphs:
attributesβ hash-ref of arguments to supply topush_subgraphfor this subgraph.nodesβ array-ref of node names to put in this subgraph.
Example, bestie:
$g->set_graph_attribute(graphviz => {
groups => [
{nodes => [1, 2], attributes => {subgraph=>{rank => 'same'}}},
],
# other graph-level attributes...
});
πΈ Graph Scope πΈ
so the graphical elements graph, node and edge all have attributes, and you can set them right when you call new(). π
inside new(), the defaults are graph => {}, node => {}, and edge => {}.
you override those with code like new(edge => {color => 'red'}).
these attributes get pushed onto a scope stack while new() is processing its parameters, and they apply from then on until you change them. they're the 'current' attributes, and they live at scope level 0 (zero).
you change the 'current' attributes by calling any of default_edge(%hash), default_graph(%hash) and default_node(%hash).
for an example, see scripts/trivial.pl (that's the one in "Scripts Shipped with this Module" in GraphViz2).
π¦ Subgraph Scope π¦
when you wanna create a subgraph, you call push_subgraph(%hash). the word push is doing a lot of work here β it's emphasising that you're moving into a new scope, and that the default attributes for the new scope get pushed onto the scope stack.
this module, just like Graphviz <http://www.graphviz.org/>, defaults to using inheritance of attributes.
meaning: the parent's 'current' attributes get combined with the parameters you pass to push_subgraph(%hash), and that generates a new set of 'current' attributes for each of the graphical elements β graph, node and edge.
after a single call to push_subgraph(%hash), those 'current' attributes live at level 1 in the scope stack.
see scripts/sub.graph.pl (in "Scripts Shipped with this Module" in GraphViz2) for an example.
another call to push_subgraph(%hash), without an intervening call to pop_subgraph(), repeats the whole process, leaving you with a set of attributes at level 2 in the scope stack.
both GraphViz2 and Graphviz <http://www.graphviz.org/> handle this situation properly. slay.
see scripts/sub.sub.graph.pl (in "Scripts Shipped with this Module" in GraphViz2) for an example.
now, at the moment, because of design defects (IMHO) in the underlying Graphviz <http://www.graphviz.org/> logic, there are some tiny problems with this:
- A global frame
I can't see how to make the graph as a whole (at level 0 in the scope stack) have a frame.
- Frame color
when you specify graph => {color => 'red'} at the parent level, the subgraph has a red frame.
I think a subgraph should control its own frame.
- Parent and child frames
when you specify graph => {color => 'red'} at the subgraph level, both that subgraph and its children have red frames.
this contradicts what happens at the global level, in that specifying color there does not give the whole graph a frame.
- Frame visibility
a subgraph whose name starts with 'cluster' is currently forced to have a frame, unless you rig it by specifying a color the same as the background.
for sample code, see scripts/sub.graph.frames.pl.
also, check the pencolor docs <http://www.graphviz.org/doc/info/attrs.html#d:pencolor> for how the color of the frame is chosen by cascading thru a set of options.
I've posted an email to the Graphviz <http://www.graphviz.org/> mailing list suggesting a new option,framecolor, so deal with this issue, including a special color of'invisible'. π
β¨ Image Maps β¨
as of V 2.43, "GraphViz2" supports image maps β both client and server side, no cap. for web use, note that these options also take effect when generating SVGs, for a much lighter-weight solution to hyperlinking graph nodes and edges. π
π The Default URL π
see the Graphviz docs for 'cmapx' <http://www.graphviz.org/doc/info/output.html#d:cmapx>.
their sample code has a dot file β x.gv β containing this line:
URL="http://www.research.att.com/base.html";
the way you set such a url in "GraphViz2" is via a new parameter to "new()". this parameter is called "im_meta" and it takes a hashref as a value. currently the only key used within that hashref is the case-sensitive "URL".
so you must do this to set a URL:
my($graph) = GraphViz2 -> new
(
...
im_meta =>
{
URL => 'http://savage.net.au/maps/demo.3.1.html', # Note: URL must be in caps.
},
);
see maps/demo.3.pl and maps/demo.4.pl for sample code.
π«Ά Typical Code π«Ά
normally you'd call "run()" like this:
$graph -> run
(
format => $format,
output_file => $output_file
);
that line was copied from scripts/cluster.pl.
to trigger image map processing, you must include 2 new parameters:
$graph -> run
(
format => $format,
output_file => $output_file,
im_format => $im_format,
im_output_file => $im_output_file
);
that line was copied from maps/demo.3.pl, and there's an identical line in maps/demo.4.pl.
π The New Parameters to run() π
im_format => $strβ expected values:'imap'(server-side) and'cmapx'(client-side). default value:'cmapx'.im_output_file => $file_nameβ the name of the output map file. default:''. if you do not set it to anything, the new image maps code is ignored.
π Sample Code π
various demos are shipped in the new maps/ directory:
each demo, when FTPed to your web server, displays some text with an image in the middle. in each case you can click on the upper oval to jump to one page, or click on the lower oval to jump to a different page, or click anywhere else in the image to jump to a third page. π iconic behaviour honestly.
demo.1.*
this set demonstrates a server-side image map but does not use "GraphViz2".
you have to run demo.1.sh which generates demo.1.map, and then you FTP the whole dir maps/ to your web server.
URL: your.domain.name/maps/demo.1.html.
demo.2.*
this set demonstrates a client-side image map but does not use "GraphViz2".
you have to run demo.2.sh which generates demo.2.map, and then you manually copy demo.2.map into demo.2.html, replacing any version of the map already present. after that you FTP the whole dir maps/ to your web server.
URL: your.domain.name/maps/demo.2.html.
demo.3.*
this set demonstrates a server-side image map using "GraphViz2" via demo.3.pl.
note line 54 of demo.3.pl which sets the default "im_format" to 'imap'.
URL: your.domain.name/maps/demo.3.html.
demo.4.*
this set demonstrates a client-side image map using "GraphViz2" via demo.4.pl.
as with demo.2.* there is some manual editing to be done.
note line 54 of demo.4.pl which sets the default "im_format" to 'cmapx'. this is the only important difference between this demo and the previous one.
there are other minor differences, in that one uses 'svg' and the other 'png'. and of course the urls of the web pages embedded in the code and in those web pages differs, just to demonstrate that the maps do indeed lead to different pages.
URL: your.domain.name/maps/demo.4.html.
π Methods π
π add_edge(from => $from_node_name, to => $to_node_name, [label => $label, %hash])
adds an edge to the graph. π¦
returns $self to allow method chaining. (iykyk)
here, [] indicate optional parameters.
add a edge from 1 node to another.
$from_node_name and $to_node_name default to ''.
%hash is any edge attributes accepted as Graphviz attributes <https://www.graphviz.org/doc/info/attrs.html>. these are validated in exactly the same way as the edge parameters in the calls to default_edge(%hash), new(edge => {}) and push_subgraph(edge => {}).
to make the edge start or finish on a port, see "combine_node_and_port".
πΈ add_node(name => $node_name, [%hash])
my $graph = GraphViz2->new(global => {combine_node_and_port => 0});
$graph->add_node(name => 'struct3', shape => 'record', label => [
{ text => "hello\\nworld" },
[
{ text => 'b' },
[
{ text => 'c{}' }, # reproduced literally
{ text => 'd', port => 'here' },
{ text => 'e' },
]
{ text => 'f' },
],
{ text => 'g' },
{ text => 'h' },
]);
adds a node to the graph. β¨
returns $self to allow method chaining.
if you want to embed newlines or double-quotes in node names or labels, see scripts/quote.pl in "Scripts Shipped with this Module" in GraphViz2.
if you want anonymous nodes, see scripts/anonymous.pl in "Scripts Shipped with this Module" in GraphViz2.
here, [] indicates an optional parameter.
%hash is any node attributes accepted as Graphviz attributes <https://www.graphviz.org/doc/info/attrs.html>. these are validated in exactly the same way as the node parameters in the calls to default_node(%hash), new(node => {}) and push_subgraph(node => {}).
the attribute name 'label' may point to a string or an arrayref.
if it is a string...
the string is the label. if the "shape" is a record, you can give any text and it will be passed for interpretation by Graphviz. this means you will need to quote < and > (port specifiers), "|" (cell separator) and "{" "}" (structure depth) with "\" to make them appear literally.
for records, the cells start horizontal. each additional layer of structure will switch the orientation between horizontal and vertical.
if it is an arrayref of strings...
- the node is forced to be a record
the actual shape, 'record' or 'Mrecord', is set globally, with:
my($graph) = GraphViz2 -> new
(
global => {record_shape => 'record'}, # Override default 'Mrecord'.
...
);
or set locally with:
$graph -> add_node(name => 'Three', label => ['Good', 'Bad'], shape => 'record');
- each element in the array defines a field in the record
these fields are combined into a single node.
- each element is treated as a label
- each label is given a port name (1 .. N) of the form
"port$port_count" - judicious use of
'{'and'}'in the label can make this record appear horizontally or vertically, and even nested
if it is an arrayref of hashrefs...
- the node is forced to be a record
the actual shape, 'record' or 'Mrecord', can be set globally or locally, as explained just above.
- each element in the array defines a field in the record
- Each element is treated as a hashref with keys
'text'and'port'
The 'port' key is optional.
- The value of the
'text'key is the label - The value of the
'port'key is the port - Judicious use of
'{'and'}'in the label can make this record appear horizontally or vertically, and even nested
See scripts/html.labels.*.pl and scripts/record.*.pl for sample code.
See also "How labels interact with ports".
For more details on this complex topic, see Records <http://www.graphviz.org/doc/info/shapes.html#record> and Ports <http://www.graphviz.org/doc/info/attrs.html#k:portPos>.
π default_edge(%hash) π
Sets the default attributes for edges you add after this call. So it's like, set the vibe now, every edge from here on picks it up. β¨
Returns $self so you can chain methods like a little icon. π
%hash is any edge attributes accepted as Graphviz attributes <https://www.graphviz.org/doc/info/attrs.html>. These get validated in exactly the same way as the edge parameters in the calls to new(edge => {}) and push_subgraph(edge => {}).
π default_graph(%hash) π
Sets default attributes for the graph. Yes bestie, the whole graph. π
Returns $self to allow method chaining.
%hash is any graph attributes accepted as Graphviz attributes <https://www.graphviz.org/doc/info/attrs.html>. These are validated in exactly the same way as the graph parameter in the calls to new(graph => {}) and push_subgraph(graph => {}).
π default_node(%hash) π
Sets default attributes for nodes added afterwards. β¨
Returns $self to allow method chaining.
%hash is any node attributes accepted as Graphviz attributes <https://www.graphviz.org/doc/info/attrs.html>. These are validated in exactly the same way as the node parameters in the calls to new(node => {}) and push_subgraph(node => {}).
π default_subgraph(%hash) π
Sets default attributes for clusters and subgraphs. π«Ά
Returns $self to allow method chaining.
%hash is any cluster or subgraph attribute accepted as Graphviz attributes <https://www.graphviz.org/doc/info/attrs.html>. These are validated in exactly the same way as the subgraph parameter in the calls to new(subgraph => {}) and push_subgraph(subgraph => {}).
π dot_input() π
Returns the output stream β all formatted nicely, slay β ready to be passed to the external program (like dot). π€
π dot_output() π
Returns the output from calling the external program (e.g. dot). π₯
You must callrun()before callingdot_output(), because it is only during the call torun()that the output of the external program gets stored in the buffer thatdot_output()controls. Norun(), no output. Periodt.
This output is available even if run() does not write the output to a file. So you never lose it, it's giving free storage. πΎ
π edge_hash() π
Returns, at the end of the run, a hashref keyed by node name β specifically the node at the arrowtail end of the hash, i.e. where the edge starts from.
Use this to get a list of all nodes and the edges which leave those nodes, the corresponding destination nodes, and the attributes of each edge. πΈοΈ
my($node_hash) = $graph -> node_hash;
my($edge_hash) = $graph -> edge_hash;
for my $from (sort keys %$node_hash)
{
my($attr) = $$node_hash{$from}{attributes};
my($s) = join(', ', map{"$_ => $$attr{$_}"} sort keys %$attr);
print "Node: $from\n";
print "\tAttributes: $s\n";
for my $to (sort keys %{$$edge_hash{$from} })
{
for my $edge (@{$$edge_hash{$from}{$to} })
{
$attr = $$edge{attributes};
$s = join(', ', map{"$_ => $$attr{$_}"} sort keys %$attr);
print "\tEdge: $from$$edge{from_port} -> $to$$edge{to_port}\n";
print "\t\tAttributes: $s\n";
}
}
}
If the caller adds the same edge two (or more) times, the attributes from each call are not coalesced (unlike "node_hash()"), but rather the attributes from each call are stored separately in an arrayref. So each edge keeps its own little identity. π
A bit more formally then, $$edge_hash{$from_node}{$to_node} is an arrayref where each element describes one edge, and which defaults to:
{
attributes => {},
from_port => $from_port,
to_port => $to_port,
}
If from_port is not provided by the caller, it defaults to '' (the empty string). If it is provided, it contains a leading ':'. Likewise for to_port. π
See scripts/report.nodes.and.edges.pl (a version of scripts/html.labels.1.pl) for a complete example.
π log([$level, $message]) π
Logs the message at the given log level. π
Returns $self to allow method chaining.
Here, [] indicate optional parameters.
$level defaults to 'debug', and $message defaults to ''.
If called with $level eq 'error', it dies with $message. π Like, full dramatic exit.
π logger($logger_object) π
Gets or sets the log object. π§
Here, [] indicates an optional parameter.
π node_hash() π
Returns, at the end of the run, a hashref keyed by node name. Use this to get a list of all nodes and their attributes. π«Ά
my($node_hash) = $graph -> node_hash;
for my $name (sort keys %$node_hash)
{
my($attr) = $$node_hash{$name}{attributes};
my($s) = join(', ', map{"$_ => $$attr{$_}"} sort keys %$attr);
print "Node: $name\n";
print "\tAttributes: $s\n";
}
If the caller adds the same node two (or more) times, the attributes from each call are coalesced (unlike "edge_hash()"), meaning all attributes from all calls get combined under the attributes sub-key. Teamwork, honestly. π
A bit more formally then, $$node_hash{$node_name} is a hashref where each element describes one node, and which defaults to:
{
attributes => {},
}
See scripts/report.nodes.and.edges.pl (a version of scripts/html.labels.1.pl) for a complete example, including usage of the corresponding "edge_hash()" method.
π pop_subgraph() π
Pop off and discard the top element of the scope stack. Bye bestie, you're done. π
Returns $self to allow method chaining.
π push_subgraph([name => $name, edge => {...}, graph => {...}, node => {...}, subgraph => {...}]) π
Sets up a new subgraph environment. β¨ New level unlocked.
Returns $self to allow method chaining.
Here, [] indicate optional parameters.
name => $name is the name to assign to the subgraph. Name defaults to ''.
So, without $name, 'subgraph {' is written to the output stream.
With $name, 'subgraph "$name" {' is written to the output stream.
Note that subgraph names beginning with 'cluster' are special to Graphviz <http://www.graphviz.org/doc/info/attrs.html#d:clusterrank>. Like, the name literally changes how it behaves. iykyk.
See scripts/rank.sub.graph.[1234].pl for the effect of various values for $name.
edge => {...} is any edge attributes accepted as Graphviz attributes <https://www.graphviz.org/doc/info/attrs.html>. These are validated in exactly the same way as the edge parameters in the calls to default_edge(%hash), new(edge => {}) and push_subgraph(edge => {}).
graph => {...} is any graph attributes accepted as Graphviz attributes <https://www.graphviz.org/doc/info/attrs.html>. These are validated in exactly the same way as the graph parameters in the calls to default_graph(%hash), new(graph => {}) and push_subgraph(graph => {}).
node => {...} is any node attributes accepted as Graphviz attributes <https://www.graphviz.org/doc/info/attrs.html>. These are validated in exactly the same way as the node parameters in the calls to default_node(%hash), new(node => {}) and push_subgraph(node => {}).
subgraph => {..} is for setting attributes applicable to clusters and subgraphs.
Currently the only subgraph attribute is "rank", but clusters have many attributes available. β¨
See the second column of the Graphviz attribute docs <https://www.graphviz.org/doc/info/attrs.html> for details.
A typical usage would be push_subgraph(subgraph => {rank => 'same'}) so that all nodes mentioned within the subgraph are constrained to be horizontally aligned. Lined up, cute. π
See scripts/rank.sub.graph.[12].pl and scripts/sub.graph.frames.pl for sample code.
π valid_attributes() π
Returns a hashref of all attributes known to this module, keyed by type to hashrefs to true values. ποΈ
Stored in this module, using Data::Section::Simple.
These attributes are used to validate attributes in many situations.
You wouldn't normally need to use this method. Fr, it's a behind-the-scenes thing. π
See scripts/report.valid.attributes.pl. See "Scripts Shipped with this Module" in GraphViz2.
π run([driver => $exe, format => $string, timeout => $integer, output_file => $output_file]) π
Runs the given program to process the output stream. π
Returns $self to allow method chaining.
Here, [] indicate optional parameters.
$driver is the name of the external program to run.
It defaults to the value supplied in the call to new(global => {driver => '...'}), which in turn defaults to File::Which's which('dot') return value.
$format is the type of output file to write.
It defaults to the value supplied in the call to new(global => {format => '...'}), which in turn defaults to 'svg'.
$timeout is the time in seconds to wait while the external program runs, before dieing with an error. β° Like, patience runs out and it's over.
It defaults to the value supplied in the call to new(global => {timeout => '...'}), which in turn defaults to 10.
$output_file is the name of the file into which the output from the external program is written.
There is no default value for$output_file. If a value is not supplied for$output_file, the only way to recover the output of the external program is to calldot_output(). So don't forget it, bestie. π
This method performs a series of tasks:
- Run the chosen external program on the
"dot_input" - Capture
STDOUTandSTDERRfrom that program - Die if
STDERRcontains anything - Copies
STDOUTto the buffer controlled by thedot_output()method - Write the captured contents of
STDOUTto$output_file, if$output_filehas a value
π stringify_attributes($context, $option) π
Returns a string suitable to writing to the output stream. π
$context is one of 'edge', 'graph', 'node', or a special string. See the code for details.
You wouldn't normally need to use this method. π
π validate_params($context, %attributes) π
Validate the given attributes within the given context. β
Also, if $context is 'subgraph', attributes are allowed to be in the 'cluster' context.
Returns $self to allow method chaining.
$context is one of 'edge', 'global', 'graph', or 'node'.
You wouldn't normally need to use this method. π
π verbose([$integer]) π
Gets or sets the verbosity level, for when a logging object is not used. π
Here, [] indicates an optional parameter.
π¦ MISC π¦
π Graphviz version supported π
So GraphViz2 targets V 2.34.0 of Graphviz (http://www.graphviz.org/). This lowkey affects the list of available attributes per graph item β node, edge, cluster, etc. β that you can use.
Wanna see the details? Peep the second column of the Graphviz attribute docs: https://www.graphviz.org/doc/info/attrs.html. That's where the tea is. β
π Supported file formats π
GraphViz2 literally parses the output of dot -T?, so what formats you get depends on your local installation. It's giving "your mileage may vary" energy. π«
β¨ Special characters in node names and labels β¨
GraphViz2 escapes these 2 characters in those contexts: [].
Escaping the 2 chars [] started with V 2.10. Before that? All of []{} were escaped, but {} are used in records to control the orientation of fields, so they should not have been escaped in the first place. Not me finding out they were doing too much. π
It would be nice to also escape | and <, but these characters are used in specifying fields and ports in records. So they stay unescaped. F.
See the next couple of points for details. π
π Ports π
Ports are what Graphviz (http://www.graphviz.org/) calls those places on the outline of a node where edges leave and terminate. Cute, right? π₯°
The Graphviz (http://www.graphviz.org/) syntax for ports is a bit unusual:
- This works:
"node_name":port5 - This doesn't:
"node_name:port5"
Let me repeat β that is Graphviz syntax, not GraphViz2 syntax. In Perl, you must do this:
$graph -> add_edge(from => 'struct1:f1', to => 'struct2:f0', color => 'blue');
You don't have to quote all node names in Graphviz (http://www.graphviz.org/), but some, such as digits, must be quoted, so I've decided to quote them all. No cap. π
π·οΈ How labels interact with ports π·οΈ
You can specify labels with ports in these ways:
- As a string
$graph -> add_node(name => 'struct3', label => "hello\nworld |{ b |{c|<here> d|e}| f}| g | h");
Here, the string contains a port (<here>), field markers (|), and orientation markers ({}).
Clearly, you must specify the field separator character | explicitly. In the next 2 cases, it is implicit.
Then you use $graph -> add_edge(...) to refer to those ports, if desired:
$graph -> add_edge(from => 'struct1:f2', to => 'struct3:here', color => 'red');
The same label is specified in the next case.
- As an arrayref of hashrefs
From scripts/record.2.pl:
$graph -> add_node(name => 'struct3', label =>
[
{
text => "hello\nworld",
},
{
text => '{b',
},
{
text => '{c',
},
{
port => '<here>',
text => 'd',
},
{
text => 'e}',
},
{
text => 'f}',
},
{
text => 'g',
},
{
text => 'h',
},
]);
Each hashref is a field, and hence you do not specify the field separator character |.
Then you use $graph -> add_edge(...) to refer to those ports, if desired. Again, from scripts/record.2.pl:
$graph -> add_edge(from => 'struct1:f2', to => 'struct3:here', color => 'red');
The same label is specified in the previous case.
- As an arrayref of strings
From scripts/html.labels.1.pl:
$graph -> add_node(name => 'Oakleigh', shape => 'record', color => 'blue',
label => ['West Oakleigh', 'East Oakleigh']);
Here, again, you do not specify the field separator character |.
What happens is that each string is taken to be the label of a field, and each field is given an auto-generated port name of the form "<port$n>", where $n starts from 1.
Here's how you refer to those ports, again from scripts/html.labels.1.pl:
$graph -> add_edge(from => 'Murrumbeena', to => 'Oakleigh:port2',
color => 'green', label => '<Drive<br/>Run<br/>Sprint>');
See also the docs for the "add_node(name => $node_name, [%hash])" method.
π Attributes for clusters π
Just use subgraph => {...}, because the code (as of V 2.22) accepts attributes belonging to either clusters or subgraphs. Slay.
An example attribute is "pencolor", which is used for clusters but not for subgraphs:
$graph->push_subgraph(
graph => {label => 'Child the Second'},
name => 'cluster Second subgraph',
node => {color => 'magenta', shape => 'diamond'},
subgraph => {pencolor => 'white'}, # White hides the cluster's frame.
);
# other nodes or edges can be added within it...
$graph->pop_subgraph;
π TODO π
- Handle edges such as
1 -> 2 -> {A B}, as seen in Graphviz (http://www.graphviz.org/)'sgraphs/directed/switch.gv
But how? π
- Validate parameters more carefully, e.g. to reject non-hashref arguments where appropriate
Some method parameter lists take keys whose value must be a hashref.
π A Extremely Short List of Other Graphing Software π
- Axis Maps (
http://www.axismaps.com/). - Polygon Map Generation (
http://www-cs-students.stanford.edu/~amitp/game-programming/polygon-map-generation/). Read more on that here (http://blogs.perl.org/users/max_maischein/2011/06/display-your-data---randompoissondisc.html). - Voronoi Applications (
http://www.voronoi.com/wiki/index.php?title=Voronoi_Applications).
π«Ά Thanks π«Ά
Many thanks are due to the people who chose to make Graphviz (http://www.graphviz.org/) Open Source. Real ones. π
And thanks to Leon Brocard (http://search.cpan.org/~lbrocard/), who wrote GraphViz, and kindly gave me co-maint of the module. King behavior. π
π’ Version Numbers π’
Version numbers < 1.00 represent development versions. From 1.00 up, they are production versions. So if it's under 1.00, it's still cooking. π©βπ³
π¦ Repository π¦
https://github.com/ronsavage/GraphViz2.git
π Author π
GraphViz2 was written by Ron Savage (ron@savage.net.au) in 2011.
Home page: http://savage.net.au/index.html.
π Copyright π
Australian copyright (c) 2011, Ron Savage.
All Programs of mine are 'OSI Certified Open Source Software';
you can redistribute them and/or modify them under the terms of
The Perl License, a copy of which is available at:
http://dev.perl.org/licenses/
And that's a wrap, bestie! This was GraphViz2(3pm), from perl v5.36.0, dated 2023-07-12. Now go make some iconic graphs. π β¨
π¦ where this page comes from
- debian trixie: libgraphviz2-perl 2.67-2 Β· Artistic-1.0-Perl OR GPL-1.0-or-later
- ubuntu noble: libgraphviz2-perl 2.67-2 Β· Artistic-1.0-Perl OR GPL-1.0-or-later
- ubuntu resolute: libgraphviz2-perl 2.67-2 Β· Artistic-1.0-Perl OR GPL-1.0-or-later
- ubuntu stonking: libgraphviz2-perl 2.67-2 Β· Artistic-1.0-Perl OR GPL-1.0-or-later
- π girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms