I made a python script to plot a directory structure using graphviz. In order to render the graph correctly, I use rankdir=LR. However, nodes having the same rank are centered.
See the following image.
As you can see, nodes like ".gitignore", ".travis.yml", etc don't have the same width because of their label. I would like to have nodes in the same rank aligned on the left side no matter their width.
I have seen other related posts like this one but I don't want to force nodes width.
Is there any other way to do it?
Thanks in advance for your answers and have a nice day.
P.S.: Here's a sample of my graph code:
strict digraph projetcStructure {
graph [overlap=false, splines=ortho, ranksep=0.05]
edge[arrowhead=none, color=black]
node[fontname="DejaVu Sans Mono", fontsize=14]
"e0ec0f89f0745e25b725768b304be71e" [shape=folder, style=filled, colorscheme=ylgnbu3, label=<<TABLE><TR><TD WIDTH="20" HEIGHT="20" FIXEDSIZE="TRUE"><IMG SRC="/mnt/e/Documents/3eme/TechnicalReport/foldertree/octicons/file-directory.png" SCALE="TRUE"/></TD><TD>TechnicalReport</TD></TR></TABLE>>, color=2]
"77a7da2a6f864418dd23f49f9c5e9804" [shape=note, style=filled, colorscheme=ylgnbu3, width=3.7555555555555555, label=<<TABLE><TR><TD WIDTH="20" HEIGHT="20" FIXEDSIZE="TRUE"><IMG SRC="/mnt/e/Documents/3eme/TechnicalReport/foldertree/octicons/git-merge.png" SCALE="TRUE"/></TD><TD>.gitignore</TD></TR></TABLE>>, color=1]
"373d5e485b88f76e79d5bab15a88759e" [shape="point", width=0, height=0]
"8e58ce33bdaaa1db79bab7080019c6fe" [shape=note, style=filled, colorscheme=ylgnbu3, width=3.7555555555555555, label=<<TABLE><TR><TD WIDTH="20" HEIGHT="20" FIXEDSIZE="TRUE"><IMG SRC="/mnt/e/Documents/3eme/TechnicalReport/foldertree/octicons/travis.png" SCALE="TRUE"/></TD><TD>.travis.yml</TD></TR></TABLE>>, color=1]
"b56aa426f8ad0d8a7704c909f2473b70" [shape="point", width=0, height=0]
"98a5f187b3527ca7f00643e7907b3453" [shape=note, style=filled, colorscheme=ylgnbu3, width=3.7555555555555555, label=<<TABLE><TR><TD WIDTH="20" HEIGHT="20" FIXEDSIZE="TRUE"><IMG SRC="/mnt/e/Documents/3eme/TechnicalReport/foldertree/octicons/book.png" SCALE="TRUE"/></TD><TD>LICENSE</TD></TR></TABLE>>, color=1]
"07adcea039d69fb282b22eebd8daf537" [shape="point", width=0, height=0]
"ede2c50901cc59eeb8987d630d055b22" [shape=note, style=filled, colorscheme=ylgnbu3, width=3.7555555555555555, label=<<TABLE><TR><TD WIDTH="20" HEIGHT="20" FIXEDSIZE="TRUE"><IMG SRC="/mnt/e/Documents/3eme/TechnicalReport/foldertree/octicons/terminal.png" SCALE="TRUE"/></TD><TD>makeScript.sh</TD></TR></TABLE>>, color=1]
"8f182d8be99f90432a2bec25a864bf19" [shape="point", width=0, height=0]
"53ccb88dccecc95c401d180aa34ac0ac" [shape=note, style=filled, colorscheme=ylgnbu3, width=3.7555555555555555, label=<<TABLE><TR><TD WIDTH="20" HEIGHT="20" FIXEDSIZE="TRUE"><IMG SRC="/mnt/e/Documents/3eme/TechnicalReport/foldertree/octicons/note.png" SCALE="TRUE"/></TD><TD>README.rst</TD></TR></TABLE>>, color=1]
"f6ffc62db5cefc5b4a1eb2e6a3731802" [shape="point", width=0, height=0]
"3f917d2353bb6c1c156b18a78f23cf71" [shape=note, style=filled, colorscheme=ylgnbu3, width=3.7555555555555555, label=<<TABLE><TR><TD WIDTH="20" HEIGHT="20" FIXEDSIZE="TRUE"><IMG SRC="/mnt/e/Documents/3eme/TechnicalReport/foldertree/octicons/file.png" SCALE="TRUE"/></TD><TD>requirements.txt</TD></TR></TABLE>>, color=1]
"acc1004c581f3b603d2a33092ffcfe61" [shape="point", width=0, height=0]
"2d05acf91c352a7ac491e18dcf65fc9c" [shape=note, style=filled, colorscheme=ylgnbu3, width=3.7555555555555555, label=<<TABLE><TR><TD WIDTH="20" HEIGHT="20" FIXEDSIZE="TRUE"><IMG SRC="/mnt/e/Documents/3eme/TechnicalReport/foldertree/octicons/settings.png" SCALE="TRUE"/></TD><TD>setup.cfg</TD></TR></TABLE>>, color=1]
"8e1760a00144fca5272f341da0873674" [shape="point", width=0, height=0]
"b78eb5bb77cb04f62506859fb1ab76f8" [shape=note, style=filled, colorscheme=ylgnbu3, width=3.7555555555555555, label=<<TABLE><TR><TD WIDTH="20" HEIGHT="20" FIXEDSIZE="TRUE"><IMG SRC="/mnt/e/Documents/3eme/TechnicalReport/foldertree/octicons/terminal.png" SCALE="TRUE"/></TD><TD>test.sh</TD></TR></TABLE>>, color=1]
"cc60f62b1b3d0cf8c266074b7139b567" [shape="point", width=0, height=0]
"3512568f6984d40e6353aa13b5029dc3" [shape=folder, style=filled, colorscheme=ylgnbu3, width=3.7555555555555555, label=<<TABLE><TR><TD WIDTH="20" HEIGHT="20" FIXEDSIZE="TRUE"><IMG SRC="/mnt/e/Documents/3eme/TechnicalReport/foldertree/octicons/mark-github.png" SCALE="TRUE"/></TD><TD>.github</TD></TR></TABLE>>, color=1]
"88ae0eb0f89e79ddff8e2b1fe470a488" [shape="point", width=0, height=0]
"9b43ae74e93bdb3dadace6084e333a27" [shape=folder, style=filled, colorscheme=ylgnbu3, width=3.7555555555555555, label=<<TABLE><TR><TD WIDTH="20" HEIGHT="20" FIXEDSIZE="TRUE"><IMG SRC="/mnt/e/Documents/3eme/TechnicalReport/foldertree/octicons/file-directory.png" SCALE="TRUE"/></TD><TD>foldertree</TD></TR></TABLE>>, color=1]
"2f9f172522c65ab785d2c23db909b36a" [shape="point", width=0, height=0]
"e7925cdfdefd09112b8b3c8b5ddde087" [shape=folder, style=filled, colorscheme=ylgnbu3, width=3.7555555555555555, label=<<TABLE><TR><TD WIDTH="20" HEIGHT="20" FIXEDSIZE="TRUE"><IMG SRC="/mnt/e/Documents/3eme/TechnicalReport/foldertree/octicons/file-directory.png" SCALE="TRUE"/></TD><TD>generated</TD></TR></TABLE>>, color=1]
"0b54fbb48c26181df9c8a5fd3bab1049" [shape="point", width=0, height=0]
"69d5f4cf4e091e008cb1f4de43692b4d" [shape=folder, style=filled, colorscheme=ylgnbu3, width=3.7555555555555555, label=<<TABLE><TR><TD WIDTH="20" HEIGHT="20" FIXEDSIZE="TRUE"><IMG SRC="/mnt/e/Documents/3eme/TechnicalReport/foldertree/octicons/file-directory.png" SCALE="TRUE"/></TD><TD>TechnicalReportGenerator</TD></TR></TABLE>>, color=1]
"c4f859f4d369226348a4eb08adab0afc" [shape="point", width=0, height=0]
"0ab55395ce4ef62f7a9f5f72201ce0ae" [shape=folder, style=filled, colorscheme=ylgnbu3, width=3.7555555555555555, label=<<TABLE><TR><TD WIDTH="20" HEIGHT="20" FIXEDSIZE="TRUE"><IMG SRC="/mnt/e/Documents/3eme/TechnicalReport/foldertree/octicons/file-directory.png" SCALE="TRUE"/></TD><TD>worklog</TD></TR></TABLE>>, color=1]
"9b4f2a57b302961cd3509dd307121cfa" [shape="point", width=0, height=0]
"77a7da2a6f864418dd23f49f9c5e9804" [shape=note, style=filled, colorscheme=ylgnbu3, width=3.7555555555555555, label=<<TABLE><TR><TD WIDTH="20" HEIGHT="20" FIXEDSIZE="TRUE"><IMG SRC="/mnt/e/Documents/3eme/TechnicalReport/foldertree/octicons/git-merge.png" SCALE="TRUE"/></TD><TD>.gitignore</TD></TR></TABLE>>, color=1]
"8e58ce33bdaaa1db79bab7080019c6fe" [shape=note, style=filled, colorscheme=ylgnbu3, width=3.7555555555555555, label=<<TABLE><TR><TD WIDTH="20" HEIGHT="20" FIXEDSIZE="TRUE"><IMG SRC="/mnt/e/Documents/3eme/TechnicalReport/foldertree/octicons/travis.png" SCALE="TRUE"/></TD><TD>.travis.yml</TD></TR></TABLE>>, color=1]
"98a5f187b3527ca7f00643e7907b3453" [shape=note, style=filled, colorscheme=ylgnbu3, width=3.7555555555555555, label=<<TABLE><TR><TD WIDTH="20" HEIGHT="20" FIXEDSIZE="TRUE"><IMG SRC="/mnt/e/Documents/3eme/TechnicalReport/foldertree/octicons/book.png" SCALE="TRUE"/></TD><TD>LICENSE</TD></TR></TABLE>>, color=1]
"ede2c50901cc59eeb8987d630d055b22" [shape=note, style=filled, colorscheme=ylgnbu3, width=3.7555555555555555, label=<<TABLE><TR><TD WIDTH="20" HEIGHT="20" FIXEDSIZE="TRUE"><IMG SRC="/mnt/e/Documents/3eme/TechnicalReport/foldertree/octicons/terminal.png" SCALE="TRUE"/></TD><TD>makeScript.sh</TD></TR></TABLE>>, color=1]
"53ccb88dccecc95c401d180aa34ac0ac" [shape=note, style=filled, colorscheme=ylgnbu3, width=3.7555555555555555, label=<<TABLE><TR><TD WIDTH="20" HEIGHT="20" FIXEDSIZE="TRUE"><IMG SRC="/mnt/e/Documents/3eme/TechnicalReport/foldertree/octicons/note.png" SCALE="TRUE"/></TD><TD>README.rst</TD></TR></TABLE>>, color=1]
"3f917d2353bb6c1c156b18a78f23cf71" [shape=note, style=filled, colorscheme=ylgnbu3, width=3.7555555555555555, label=<<TABLE><TR><TD WIDTH="20" HEIGHT="20" FIXEDSIZE="TRUE"><IMG SRC="/mnt/e/Documents/3eme/TechnicalReport/foldertree/octicons/file.png" SCALE="TRUE"/></TD><TD>requirements.txt</TD></TR></TABLE>>, color=1]
"2d05acf91c352a7ac491e18dcf65fc9c" [shape=note, style=filled, colorscheme=ylgnbu3, width=3.7555555555555555, label=<<TABLE><TR><TD WIDTH="20" HEIGHT="20" FIXEDSIZE="TRUE"><IMG SRC="/mnt/e/Documents/3eme/TechnicalReport/foldertree/octicons/settings.png" SCALE="TRUE"/></TD><TD>setup.cfg</TD></TR></TABLE>>, color=1]
"b78eb5bb77cb04f62506859fb1ab76f8" [shape=note, style=filled, colorscheme=ylgnbu3, width=3.7555555555555555, label=<<TABLE><TR><TD WIDTH="20" HEIGHT="20" FIXEDSIZE="TRUE"><IMG SRC="/mnt/e/Documents/3eme/TechnicalReport/foldertree/octicons/terminal.png" SCALE="TRUE"/></TD><TD>test.sh</TD></TR></TABLE>>, color=1]
"3512568f6984d40e6353aa13b5029dc3" [shape=folder, style=filled, colorscheme=ylgnbu3, width=3.7555555555555555, label=<<TABLE><TR><TD WIDTH="20" HEIGHT="20" FIXEDSIZE="TRUE"><IMG SRC="/mnt/e/Documents/3eme/TechnicalReport/foldertree/octicons/mark-github.png" SCALE="TRUE"/></TD><TD>.github</TD></TR></TABLE>>, color=1]
"9b43ae74e93bdb3dadace6084e333a27" [shape=folder, style=filled, colorscheme=ylgnbu3, width=3.7555555555555555, label=<<TABLE><TR><TD WIDTH="20" HEIGHT="20" FIXEDSIZE="TRUE"><IMG SRC="/mnt/e/Documents/3eme/TechnicalReport/foldertree/octicons/file-directory.png" SCALE="TRUE"/></TD><TD>foldertree</TD></TR></TABLE>>, color=1]
"e7925cdfdefd09112b8b3c8b5ddde087" [shape=folder, style=filled, colorscheme=ylgnbu3, width=3.7555555555555555, label=<<TABLE><TR><TD WIDTH="20" HEIGHT="20" FIXEDSIZE="TRUE"><IMG SRC="/mnt/e/Documents/3eme/TechnicalReport/foldertree/octicons/file-directory.png" SCALE="TRUE"/></TD><TD>generated</TD></TR></TABLE>>, color=1]
"69d5f4cf4e091e008cb1f4de43692b4d" [shape=folder, style=filled, colorscheme=ylgnbu3, width=3.7555555555555555, label=<<TABLE><TR><TD WIDTH="20" HEIGHT="20" FIXEDSIZE="TRUE"><IMG SRC="/mnt/e/Documents/3eme/TechnicalReport/foldertree/octicons/file-directory.png" SCALE="TRUE"/></TD><TD>TechnicalReportGenerator</TD></TR></TABLE>>, color=1]
"0ab55395ce4ef62f7a9f5f72201ce0ae" [shape=folder, style=filled, colorscheme=ylgnbu3, width=3.7555555555555555, label=<<TABLE><TR><TD WIDTH="20" HEIGHT="20" FIXEDSIZE="TRUE"><IMG SRC="/mnt/e/Documents/3eme/TechnicalReport/foldertree/octicons/file-directory.png" SCALE="TRUE"/></TD><TD>worklog</TD></TR></TABLE>>, color=1]
{rank=same; "373d5e485b88f76e79d5bab15a88759e" "b56aa426f8ad0d8a7704c909f2473b70" "07adcea039d69fb282b22eebd8daf537" "8f182d8be99f90432a2bec25a864bf19" "f6ffc62db5cefc5b4a1eb2e6a3731802" "acc1004c581f3b603d2a33092ffcfe61" "8e1760a00144fca5272f341da0873674" "cc60f62b1b3d0cf8c266074b7139b567" "88ae0eb0f89e79ddff8e2b1fe470a488" "2f9f172522c65ab785d2c23db909b36a" "0b54fbb48c26181df9c8a5fd3bab1049" "c4f859f4d369226348a4eb08adab0afc" "9b4f2a57b302961cd3509dd307121cfa"}
"e0ec0f89f0745e25b725768b304be71e" -> "373d5e485b88f76e79d5bab15a88759e"
"373d5e485b88f76e79d5bab15a88759e" -> "b56aa426f8ad0d8a7704c909f2473b70"
"b56aa426f8ad0d8a7704c909f2473b70" -> "07adcea039d69fb282b22eebd8daf537"
"07adcea039d69fb282b22eebd8daf537" -> "8f182d8be99f90432a2bec25a864bf19"
"8f182d8be99f90432a2bec25a864bf19" -> "f6ffc62db5cefc5b4a1eb2e6a3731802"
"f6ffc62db5cefc5b4a1eb2e6a3731802" -> "acc1004c581f3b603d2a33092ffcfe61"
"acc1004c581f3b603d2a33092ffcfe61" -> "8e1760a00144fca5272f341da0873674"
"8e1760a00144fca5272f341da0873674" -> "cc60f62b1b3d0cf8c266074b7139b567"
"cc60f62b1b3d0cf8c266074b7139b567" -> "88ae0eb0f89e79ddff8e2b1fe470a488"
"88ae0eb0f89e79ddff8e2b1fe470a488" -> "2f9f172522c65ab785d2c23db909b36a"
"2f9f172522c65ab785d2c23db909b36a" -> "0b54fbb48c26181df9c8a5fd3bab1049"
"0b54fbb48c26181df9c8a5fd3bab1049" -> "c4f859f4d369226348a4eb08adab0afc"
"c4f859f4d369226348a4eb08adab0afc" -> "9b4f2a57b302961cd3509dd307121cfa"
{rank=same; "77a7da2a6f864418dd23f49f9c5e9804" "8e58ce33bdaaa1db79bab7080019c6fe" "98a5f187b3527ca7f00643e7907b3453" "ede2c50901cc59eeb8987d630d055b22" "53ccb88dccecc95c401d180aa34ac0ac" "3f917d2353bb6c1c156b18a78f23cf71" "2d05acf91c352a7ac491e18dcf65fc9c" "b78eb5bb77cb04f62506859fb1ab76f8" "3512568f6984d40e6353aa13b5029dc3" "9b43ae74e93bdb3dadace6084e333a27" "e7925cdfdefd09112b8b3c8b5ddde087" "69d5f4cf4e091e008cb1f4de43692b4d" "0ab55395ce4ef62f7a9f5f72201ce0ae"}
"373d5e485b88f76e79d5bab15a88759e" -> "77a7da2a6f864418dd23f49f9c5e9804"
"b56aa426f8ad0d8a7704c909f2473b70" -> "8e58ce33bdaaa1db79bab7080019c6fe"
"07adcea039d69fb282b22eebd8daf537" -> "98a5f187b3527ca7f00643e7907b3453"
"8f182d8be99f90432a2bec25a864bf19" -> "ede2c50901cc59eeb8987d630d055b22"
"f6ffc62db5cefc5b4a1eb2e6a3731802" -> "53ccb88dccecc95c401d180aa34ac0ac"
"acc1004c581f3b603d2a33092ffcfe61" -> "3f917d2353bb6c1c156b18a78f23cf71"
"8e1760a00144fca5272f341da0873674" -> "2d05acf91c352a7ac491e18dcf65fc9c"
"cc60f62b1b3d0cf8c266074b7139b567" -> "b78eb5bb77cb04f62506859fb1ab76f8"
"88ae0eb0f89e79ddff8e2b1fe470a488" -> "3512568f6984d40e6353aa13b5029dc3"
"2f9f172522c65ab785d2c23db909b36a" -> "9b43ae74e93bdb3dadace6084e333a27"
"0b54fbb48c26181df9c8a5fd3bab1049" -> "e7925cdfdefd09112b8b3c8b5ddde087"
"c4f859f4d369226348a4eb08adab0afc" -> "69d5f4cf4e091e008cb1f4de43692b4d"
"9b4f2a57b302961cd3509dd307121cfa" -> "0ab55395ce4ef62f7a9f5f72201ce0ae"
}
After a few researches and thanks to @TomServo, I have been able to "fix" the nodes width.
I use the following code. ImageFont doc can be found here
font = ImageFont.truetype('DejaVuSansMono.ttf', FONT_SIZE)
width = font.getsize("YOUR STRING TO MEASURE HERE")[0]
# convert pixels to inches and add extra space for icons and margins
# icon size is defined as points which is a 72's of an inch
width = width / GRAPHVIZ_DPI + ((1 / 72) * 20 * 2)
Then the result:
Sorry, but I don't think there is a way to achieve the effect you want without fixing the width of the nodes in a rank. You may want to reconsider though because making your nodes the same width really makes the graph much neater:
strict digraph projetcStructure {
graph [overlap=false, splines=ortho, ranksep=0.05, rankdir=LR]
edge[arrowhead=none, color=black]
"2f173bfac231270e0418210e8a3552d2" [shape=folder, style=filled, colorscheme=ylgnbu3, label=<<TABLE><TR><TD WIDTH="20" HEIGHT="20" FIXEDSIZE="TRUE"><IMG SRC="/mnt/e/Documents/3eme/TechnicalReport/FolderTree/octicons/file-directory.png" SCALE="TRUE"/></TD><TD>TechnicalReport</TD></TR></TABLE>>, color=3, width=2.0]
"6b9ec0d7104a108a26ba34f9672f35a5" [shape=note, style=filled, colorscheme=ylgnbu3, label=<<TABLE><TR><TD WIDTH="20" HEIGHT="20" FIXEDSIZE="TRUE"><IMG SRC="/mnt/e/Documents/3eme/TechnicalReport/FolderTree/octicons/git-merge.png" SCALE="TRUE"/></TD><TD>.gitignore</TD></TR></TABLE>>, color=2, width=2.0]
"6a20aaea09ab2aac28e4b8f50f46d9e6" [shape="point", width=0, height=0]
"ad9d05da3ebed612a90ab85a30d039d7" [shape=note, style=filled, colorscheme=ylgnbu3, label=<<TABLE><TR><TD WIDTH="20" HEIGHT="20" FIXEDSIZE="TRUE"><IMG SRC="/mnt/e/Documents/3eme/TechnicalReport/FolderTree/octicons/travis.png" SCALE="TRUE"/></TD><TD>.travis.yml</TD></TR></TABLE>>, color=2, width=2.0]
"2bef36c3d9fd70fec1b1d395ec891948" [shape="point", width=0, height=0]
"023bdc0f2f34c3d14a028c1190295079" [shape=note, style=filled, colorscheme=ylgnbu3, label=<<TABLE><TR><TD WIDTH="20" HEIGHT="20" FIXEDSIZE="TRUE"><IMG SRC="/mnt/e/Documents/3eme/TechnicalReport/FolderTree/octicons/book.png" SCALE="TRUE"/></TD><TD>LICENSE</TD></TR></TABLE>>, color=2, width=2.0]
"1a7aeec41293922786ed460f863c197c" [shape="point", width=0, height=0]
"418903823b3792736b96279643175a80" [shape=note, style=filled, colorscheme=ylgnbu3, label=<<TABLE><TR><TD WIDTH="20" HEIGHT="20" FIXEDSIZE="TRUE"><IMG SRC="/mnt/e/Documents/3eme/TechnicalReport/FolderTree/octicons/terminal.png" SCALE="TRUE"/></TD><TD>makeScript.sh</TD></TR></TABLE>>, color=2, width=2.0]
Notice the width=2.0
at the end of the first few nodes. This makes them the same width and cleans up the ragged look a little.
Also, do put rankdir=LR
near the top as shown, and as you mentioned in your question.
To "precompute" the width necessary, on a Windows system I would use the Graphics namespace to determine width, something like this:
private void MeasureStringWidth(PaintEventArgs e)
{
// Set up string.
string measureString = "Measure String";
Font stringFont = new Font("Arial", 16);
// Set maximum width of string.
int stringWidth = 200;
// Measure string.
SizeF stringSize = new SizeF();
stringSize = e.Graphics.MeasureString(measureString, stringFont, stringWidth);
// Draw rectangle representing size of string.
e.Graphics.DrawRectangle(new Pen(Color.Red, 1), 0.0F, 0.0F, stringSize.Width, stringSize.Height);
// Draw string to screen.
e.Graphics.DrawString(measureString, stringFont, Brushes.Black, new PointF(0, 0));
}
Then convert the stringSize.Width
value to inches for example. Add a little padding, add a little extra for your included icon graphics and then use that for your uniform width.
Finally, if you're not on a Windows system, or not using .NET, there may be other ways to measure the width of a string, as in the example with python and tkinter. So don't give up, you can do it!