Writing huge graphs in Graphviz is tedious. One day I will write my graphs in a custom format, which I will then transform into DOT format, but currently I want to find out the ways to write DOT files manually as succinctly as possible. I currently know of only one syntax shortcut: a -- {b c}; is equivalent to a -- b; a -- c;. In the DOT file currently there are many repeating patterns, for example many edges have the same label. Can I write something like:
// something that expands -m> into [label=meaning]
"English/cat" -m> "Meaning/cat_(Felidae)";
"English/cat" -m> "Meaning/domestic_cat";
"English/cat" -m> "Meaning/catfish";
"English/cat" -m> "Meaning/jazz_player";
"English/cat" -m> "Meaning/cat_(nautical)";
So that it would be equivalent to the below:
"English/cat" -> "Meaning/cat_(Felidae)" [label=meaning];
"English/cat" -> "Meaning/domestic_cat" [label=meaning];
"English/cat" -> "Meaning/catfish" [label=meaning];
"English/cat" -> "Meaning/jazz_player" [label=meaning];
"English/cat" -> "Meaning/cat_(nautical)" [label=meaning];
Is this possible? Are there any other possible syntax shortcuts in Graphviz that would make DOT files simpler and shorter? I would be happy if you could compile all such methods in the answers.
The dot language allows default attributes for graphs, nodes and edges. These default attributes are valid for any new graph/node/edge defined after the default attributes, or until other default attributes are defined.
Your example could be rewritten this way:
// label for all edges from here on
edge [label=meaning];
"English/cat" -> "Meaning/cat_(Felidae)";
"English/cat" -> "Meaning/domestic_cat";
"English/cat" -> "Meaning/catfish";
"English/cat" -> "Meaning/jazz_player";
"English/cat" -> "Meaning/cat_(nautical)";
// no/empty label from here on
edge[label=""];
"English/cat" -> "Other";
The same applies for nodes and graphs, just use graph [a=b, c=d, ...] and node [a=b, c=d, ...].
To expand on marapet's answer, you can put default node/edge/graph attributes into a subgraph (see DOT language documentation), so the attribute changes will affect only the scope of the subgraph. Here, only edges c -> d and e -> f will have a label "meaning" attached, while edges a -> b and g -> h will have no label:
a -> b;
subgraph {
edge[label="meaning"];
c -> d;
e -> f;
}
g -> h;
Related
I would like to indicate that a node should be there, but is currently lacking in the process.
Intuitively I would like to cross it out as shown in below image (now done manually in Paint):
Is there a node attribute in Graphviz that I can use for this?
I can't find an attribute or node shape to do what you want, but here are two ways to do it:
build an image outside of Graphviz (with the text and the X) and use the image attribute to use the image as the node (yes, a pain if you want to do this frequently):
b [image="myB.png"]
For every X'd out node, add 2 new edges from .ne to .sw and .nw to .se (see below) Each with this (new) attribute: straightline=1. Then run this command:
dot -Tdot Xout2.gv |gvpr -f straightline.gvpr -c | neato -n2 -Tpng >out.png
Where this is straightline.gvpr:
E[straightline==1]{
int i, n;
string pt[int];
double x1, y1, x2, y2, xI1, yI1, xI2, yI2;
n=split($.pos, pt, " ");
for (i=0;i<=1;i++){
if (match(pt[i],"e,")>=0){
print ("// BEFORE: ", pt[i]);
pt[n-1]=substr(pt[i],2);
print ("// AFTER: ", pt[i]);
pt[i]=pt[i+1];
}
}
for (i=0;i<=1;i++){
if (match(pt[i],"s,")>=0){
pt[0]=substr(pt[i],2);
}
}
sscanf (pt[0], "%f,%f", &x1, &y1);
sscanf (pt[n-1], "%f,%f", &x2, &y2);
xI1=x1+(x2-x1)*.3;
yI1=y1+(y2-y1)*.3;
xI2=x1+(x2-x1)*.7;
yI2=y1+(y2-y1)*.7;
$.pos=sprintf("%s %.3f,%.3f %.3f,%.3f %s", pt[0], xI1, yI1, xI2, yI2, pt[n-1]);
}
From this input:
digraph X{
graph [outputorder=edgefirst]
b [ label="X me"]
a -> b -> c
a -> d
d -> c
e -> f
g -> i -> k
edge [color="#ff000080" penwidth=2] // note translucent color
b:ne -> b:sw [straightline=1]
b:se -> b:nw [straightline=1]
edge [color="green" penwidth=2]
e:n -> e:s [straightline=1]
f:w -> f:se [straightline=1]
edge [color="orange" penwidth=2]
g:n -> g:se [dir=back straightline=1]
edge [color="blue" penwidth=2]
g:n -> g:sw [dir=back straightline=1]
i:e -> i:w [dir=none straightline=1]
k -> k:s [dir=both straightline=1]
}
Sorry, convoluted, but it works.
While the answer of sroush gives me the exact output I need, it requires that I understand how to introduce gvpr in my workflow which will take a bit of time.
In the meantime I came up with a dot only approach, which approximates crossing out a node sufficiently for my purpose.
In below graph I would like to cross out the node Some process:
digraph graphname {
rankdir=LR
node [fillcolor="lightblue3", style="filled"]
a
c
d
b [label="Some\nprocess"]
a -> b -> c
a -> d -> c
{rank=same a;d}
}
To do so I change:
the nodestyle of the Some process node to have a diagonal hard gradient
use a HTML-like label to strikethrough the text
Make the fontcolor and node outline a shade of gray
digraph graphname {
rankdir=LR
node [fillcolor="lightblue3", style="filled"]
a
c
d
node [fillcolor="lightblue3;0.5:white", style="filled", fontcolor="gray50", color="gray50", gradientangle=100]
b [label=<<s>Some<br/>process</s>>]
a -> b -> c
a -> d -> c
{rank=same a;d}
}
I have the following source code for a graph in dot:
digraph name {
rankdir="LR";
node [shape="record"];
1 [label="OUTPUT"];
A [label="FWD|<i>i|<r_in>r_in|<r_out>r_out|<o>o"];
B [label="FIFO|<r_in>r_in|<o>o"];
C [label="Cons|<i>i|<r_out>r_out|<o>o"];
A:o:e -> C:i:w;
C:r_out:w -> A:r_in:e;
B:o:e -> A:i:w;
C:o:e -> 1:w;
A:r_out:w -> B:r_in:e;
}
It consists of 4 nodes, which essentially could be placed one after another, in the order B -> A -> C -> OUTPUT. If dot would place the nodes in this order, only few edge would cross between two nodes.
However, calling dot like this:
dot mygraph.dot -Tpng -o mygraph.png
creates the following mess:
Edit: It seems, the order of the nodes in the source is important. However, as the source is generated from a program, outputting its internal signal flow structure, I cannot rely on it to put the nodes in the right order. I thought, dot and its graph layout engine can figure out on its own, which nodes are the first ones, such that the wires do not cross.
Just define the nodes in the desired order:
digraph name {
rankdir="LR";
node [shape="record"];
B [label="FIFO|<r_in>r_in|<o>o"];
A [label="FWD|<i>i|<r_in>r_in|<r_out>r_out|<o>o"];
C [label="Cons|<i>i|<r_out>r_out|<o>o"];
1 [label="OUTPUT"];
A:o:e -> C:i:w;
C:r_out:w -> A:r_in:e;
B:o:e -> A:i:w;
C:o:e -> 1:w;
A:r_out:w -> B:r_in:e;
}
yields
I use graphviz to draw commands tree. By default it's merging nodes with same name. How to prohibit this?
Example:
I have a code:
strict digraph 2 {
rankdir=LR;
SHOW_CONFIGURATION -> INTERFACES_eth;
SHOW_CONFIGURATION -> INTERFACES_vlan;
SHOW_CONFIGURATION -> INTERFACES_lag;
SHOW_CONFIGURATION -> INTERFACES_eth -> DESCRIPTION;
SHOW_CONFIGURATION -> INTERFACES_vlan -> DESCRIPTION;
SHOW_CONFIGURATION -> INTERFACES_lag -> DESCRIPTION;
SHOW_CONFIGURATION -> INTERFACES_eth -> IPV4;
SHOW_CONFIGURATION -> INTERFACES_vlan -> IPV4;
SHOW_CONFIGURATION -> INTERFACES_lag -> IPV4;
}
Result of drawing with command dot -Tsvg -o cli_tree.svg SHOW_CONFIGURATION.dot:
But i need to draw it without merging of same subcommand nodes, like in this image:
.
Please, help me to know how can i draw my graph like so.
By default, graphviz uses the node id as label. If distinct nodes need to have the same label, the label has to be defined explicitely.
I also find it sometimes useful to define first all nodes, then the edges between those nodes.
strict digraph 2 {
rankdir=LR;
//Nodes
cfg [label="SHOW_CONFIGURATION"];
eth [label="INTERFACES_eth"];
vlan [label="INTERFACES_vlan"];
lag [label="INTERFACES_lag"];
node[label="DESCRIPTION"];
d1;d2;d3;
node[label="IPV4"];
i1;i2;i3;
// Edges
cfg -> {eth; vlan; lag;}
eth -> {d1; i1;}
vlan -> {d2; i2;}
lag -> {d3; i3}
}
In this example, the instruction node[...] defines default attributes for all new nodes after this instruction.
I am making a graph using graphviz dot.
like this
digraph protocol {
label="Protocol workflow";
node[shape=box, style=rounded];
a[label="Byte received"];
b[label="Is start delimiter?", shape=diamond];
c[label="Recv length [n, 1B]"]; c1[label="Set count=n"];
d[label="Recv 1 payload byte and decrement count"];
e[label="count > 0", shape=diamond];
f[label="Recv CRC [2B]"];
g[label="Is CRC valid?", shape=diamond];
a -> b -> c -> c1 -> d -> e -> f -> g;
e -> d;
}
Problem is that to some arrows I need to add yes no labels but I don't know how I could do that.
You may add labels to edges like this:
e -> f [label="Yes"];
I am using graphviz for the first time. I just need a tree layout so that all the childs are at the same level.
For example,
A->B
A->C
A->D
THEN B, C AND D SHOULD BE AT THE SAME LEVEL.
Following is the code I am using.
digraph unix {
size="6,6";
node [color=lightblue2, style=filled];
"A:1000" -> "B:300";
"A:1000" -> "C:300";
"A:1000" -> "D:200";
"B:300" -> "E:140";
"B:300" -> "F:164";
"B:300" -> "G:75";
"C:300" -> "H:135";
"C:300" -> "I:91";
"D:200" -> "E:140";
"D:200" -> "F:164";
"D:200" -> "G:75";
"E:140" -> "F:164";
"E:140" -> "G:75";
"F:164" -> "G:75";
"G:75" -> "H:135";
"H:135" -> "I:91";
}
How do I make sure that the childs are at the same level?
To get nodes on the same level, say "B:300" and "C:300", add the following line:
{rank=same; "B:300" "C:300"}
The graph you presented does not represent a tree, but a directed acyclic graph (In a tree there is only one distinct path between every pair of nodes). If your input would have been a tree, then just using dot would produce what you want. If you also want to add non-tree edges, like "C:300" -> "H:135" in your example, you could specify a lower weight for them, to make sure that dot doesn't try to optimize the layout with respect to these edges.
"C:300" -> "H:135" [weight=0];
"C:300" -> "I:91" [weight=0];
Note that these two edges become very long (and to dot, ugly) with this setting, and this is the reason why the node "C:300" is placed as it is in your original graph.