31

Good day everyone -

Is there a fast and efficient way to draw directory tree structures (aka depth-indented listings) like the unix's tree command, that is, is there a way to draw the following using TikZ?

root
  |
  |--- dir1
  |     |
  |     |-- a1
  |     |-- a2
  |--- dir2
  |--- dir3

I've checked tikz-tree and tikz-qtree, but the packages always want to grow the tree balanced and not down then to the right.

Any advice is truly appreciated.

Thanks.

2
  • 3
    If TikZ is not really obligatory, then try dirtree package.
    – przemoc
    Commented Jul 20, 2011 at 22:38
  • Using TikZ is tricky here: you can get part of the way by using grow with three children = one child at (2em,-2em) and two children at (2em,-2em) and (2em, -4em) and edge from parent to draw perpendicular lines. But as the manual says, tree layout occurs without reasoning about the size of the children. tikz-qtree handles reasoning about the size of the children, and the grow'=right option will grow trees rightwards, but balanced instead of bottom-heavy. So some variant of tikz-qtree's code should work...but I don't understand its code well enough to write that variant.
    – Ben Lerner
    Commented Jul 21, 2011 at 0:53

5 Answers 5

33

I just came across this thanks to the duplicate at Vertical trees with TikZ. The following code seems to work, though I will admit to not testing it fully and making up the numbers so that it just works.

Let's start with some output:

TikZ version of dirtree

Here's the code:

\documentclass[border=10]{standalone}
\usepackage{tikz}

\makeatletter
\newcount\dirtree@lvl
\newcount\dirtree@plvl
\newcount\dirtree@clvl
\def\dirtree@growth{%
  \ifnum\tikznumberofcurrentchild=1\relax
  \global\advance\dirtree@plvl by 1
  \expandafter\xdef\csname dirtree@p@\the\dirtree@plvl\endcsname{\the\dirtree@lvl}
  \fi
  \global\advance\dirtree@lvl by 1\relax
  \dirtree@clvl=\dirtree@lvl
  \advance\dirtree@clvl by -\csname dirtree@p@\the\dirtree@plvl\endcsname
  \pgf@xa=1cm\relax
  \pgf@ya=-1cm\relax
  \pgf@ya=\dirtree@clvl\pgf@ya
  \pgftransformshift{\pgfqpoint{\the\pgf@xa}{\the\pgf@ya}}%
  \ifnum\tikznumberofcurrentchild=\tikznumberofchildren
  \global\advance\dirtree@plvl by -1
  \fi
}

\tikzset{
  dirtree/.style={
    growth function=\dirtree@growth,
    every node/.style={anchor=north},
    every child node/.style={anchor=west},
    edge from parent path={(\tikzparentnode\tikzparentanchor) |- (\tikzchildnode\tikzchildanchor)}
  }
}
\makeatother
\begin{document}
\begin{tikzpicture}[dirtree]
\node {toplevel} 
    child { node {Foo}
            child { node {foo} }
            child { node {bar} }
            child { node {baz} }
    }
    child { node {Bar}
        child { node {foo} }
        child { node {foo} }
        child { node {foo} }
        child { node {foo} }
        child { node {bar} }
        child { node {baz} }
    }
    child { node {Baz}
        child { node {foo} }
        child { node {bar} }
        child { node {baz} }
    };
\end{tikzpicture}
\end{document}

We define a new growth function. The idea is that we have a global counter that keeps track of how many nodes we've processed so far and position the children according to this. The main problem is that the way trees are built: the origin for each branch is the parent node. So we want to apply an absolute transformation (the total height from the root) but have to take into account the relative transformation first. This takes a bit of bookkeeping, but once that's figured out then the rest is ... plain sailing.

The edges are the easy bit - we simply define them to be |-, ie vertical and then horizontal.

3
  • I’ve added node anchors for better appearance with longer text. There’s also another solution on texample.net, now: texample.net/tikz/examples/filesystem-tree.
    – Speravir
    Commented Dec 28, 2011 at 17:54
  • @Speravir Good addition, thanks. I also agree with your comment on the texample example! I'm not known for the elegance of my code here. If you can think of any further improvements to make it easier to customise, then feel free to edit the answer (or, if you feel you've done a lot of improving, then add another answer with the improved code). Commented Dec 28, 2011 at 20:09
  • 2
    Worth noting the modifications from tex.stackexchange.com/q/89129/86 for better arranging the horizontal alignment. Commented Jan 4, 2013 at 11:21
20

Here are two concepts doing it with TikZ. The first one doesn't draw enough lines, the second one draws too many. I guess one needs counters for each level. Those counters would be increased if the same level occurs again and draw a line to it, while they would reset when a higher level is encounterd, e.g. like one could principally do with

\usepackage{chngcntr}
\counterwithin{leveltwo}{levelone}

Probably I'll get to it this week, but don't count on it. Here's what I have so far:

\documentclass[parskip,14pt]{scrartcl}
\usepackage[margin=15mm]{geometry}
\usepackage{tikz}

\newcounter{treeline}

\newcommand{\treeroot}[1]{% Title
\node[above] at (0,0) {#1};%
\setcounter{treeline}{0}
}

\newcommand{\treeentry}[2]{% Title, Level
\draw[->] (#2-1,-\value{treeline}/2) -- (#2-1,-\value{treeline}/2-0.5) -- (#2+0.5,-\value{treeline}/2-0.5) node[right] {#1};
\stepcounter{treeline}
}

\newcommand{\altentry}[2]{% Title, Level
\draw[->] (#2-1,-\value{treeline}/2) -- (#2-1,-\value{treeline}/2-0.5) -- (#2+0.5,-\value{treeline}/2-0.5) node[right] {#1};
\foreach \x in {1,...,#2}
{   \draw (\x-1,-\value{treeline}/2) -- (\x-1,-\value{treeline}/2-0.5);
}
\stepcounter{treeline}
}

\begin{document}

\begin{tikzpicture}
\treeroot{$\sqrt{of\ all\ evil}$}
\treeentry{Bla}{1}
\treeentry{Bla}{2}
\treeentry{Bla}{3}
\treeentry{Bla}{2}
\treeentry{Bla}{2}
\treeentry{Bla}{3}
\treeentry{Bla}{3}
\treeentry{Bla}{3}
\treeentry{Bla}{3}
\treeentry{Bla}{3}
\treeentry{Bla}{4}
\treeentry{Bla}{4}
\treeentry{Bla}{4}
\treeentry{Bla}{4}
\treeentry{Bla}{4}
\treeentry{Bla}{4}
\treeentry{Bla}{1}
\treeentry{Bla}{1}
\treeentry{Bla}{2}
\treeentry{Bla}{3}
\treeentry{Bla}{2}
\treeentry{Bla}{2}
\treeentry{Bla}{3}
\treeentry{Bla}{1}
\end{tikzpicture}
\hspace{2cm}
\begin{tikzpicture}
\treeroot{$\sqrt{of\ all\ evil}$}
\altentry{Bla}{1}
\altentry{Bla}{2}
\altentry{Bla}{3}
\altentry{Bla}{2}
\altentry{Bla}{2}
\altentry{Bla}{3}
\altentry{Bla}{3}
\altentry{Bla}{3}
\altentry{Bla}{3}
\altentry{Bla}{3}
\altentry{Bla}{4}
\altentry{Bla}{4}
\altentry{Bla}{4}
\altentry{Bla}{4}
\altentry{Bla}{4}
\altentry{Bla}{4}
\altentry{Bla}{1}
\altentry{Bla}{1}
\altentry{Bla}{2}
\altentry{Bla}{3}
\altentry{Bla}{2}
\altentry{Bla}{2}
\altentry{Bla}{3}
\altentry{Bla}{1}
\end{tikzpicture}

\end{document}

And the result:

enter image description here

15

Update: Forest now provides this style more-or-less out-of-the-box. If you need a solution for older versions of Forest (for whatever reason), see my original answer below for the definition of a suitable style.


Forest v.2

Here's a forest solution which uses the folder style provided by the edges library. This requires version 2 of Forest.

\documentclass[tikz, border=5pt, multi]{standalone}
\usepackage[edges]{forest}
\begin{document}
\begin{forest}
  for tree={%
    folder,
    grow'=0,
    fit=band,
  }
  [root
    [bin
    ]
    [etc
      [X11]
      [cron.d]
      [default]
    ]
    [local
      [bin]
      [etc]
      [texlive
        [2014
          [bin]
          [readme-html.dir]
          [texmf-config]
          [texmf-dist]
          [texmf-var]
        ]
        [texmf-local]
      ]
    ]
    [usr
      [bin]
      [include]
      [share]
    ]
    [var
      [cache]
      [log]
      [spool]
      [tmp]
    ]
  ]
\end{forest}
\end{document}

almost-out-of-the-box with v.2

Forest v.1

Here's my original forest solution which defines a new style dir tree which can be used to typeset trees in this format with version 1 of forest.

\documentclass[tikz, border=5pt, multi]{standalone}
\usepackage{forest}
\usetikzlibrary{arrows.meta}
\forestset{
  dir tree/.style={
    for tree={
      parent anchor=south west,
      child anchor=west,
      anchor=mid west,
      inner ysep=1pt,
      grow'=0,
      align=left,
      edge path={
        \noexpand\path [draw, \forestoption{edge}] (!u.parent anchor) ++(1em,0) |- (.child anchor)\forestoption{edge label};
      },
      font=\sffamily,
      if n children=0{}{
        delay={
          prepend={[,phantom, calign with current]}
        }
      },
      fit=band,
      before computing xy={
        l=2em
      }
    },
  }
}
\begin{document}
\begin{forest}
  dir tree
  [root
    [bin
    ]
    [etc
      [X11]
      [cron.d]
      [default]
    ]
    [local
      [bin]
      [etc]
      [texlive
        [2014
          [bin]
          [readme-html.dir]
          [texmf-config]
          [texmf-dist]
          [texmf-var]
        ]
        [texmf-local]
      ]
    ]
    [usr
      [bin]
      [include]
      [share]
    ]
    [var
      [cache]
      [log]
      [spool]
      [tmp]
    ]
  ]
\end{forest}
\end{document}

dir tree

5
  • The suggested edit creates a tree which looks worse in my opinion. While it increases the distance from e.g. var to var/cache, it increases it too much and the result is rather unsightly gaps in the trunk and branches. (Not gaps in the lines drawn but gaps between items.) If the edit is approved, I will roll it back.
    – cfr
    Commented Aug 23, 2015 at 19:11
  • is there a chance to split this long tree to two halves using its own setting?
    – Amir
    Commented May 10, 2016 at 14:58
  • @Amir See tex.stackexchange.com/questions/296692/…. Related: tex.stackexchange.com/questions/300413/….
    – cfr
    Commented May 10, 2016 at 21:43
  • @Amir But note that the style itself is now supported out-of-the-box by Forest.
    – cfr
    Commented May 10, 2016 at 21:45
  • @Amir See edited answer above for the out-of-the-box version. If you have trouble applying the answers on splitting and you really want to do it, I'd suggest asking a new question.
    – cfr
    Commented May 10, 2016 at 21:58
12

Here is my take on it:

\documentclass{standalone}
\usepackage{tikz}
\usepackage{fontawesome}
\begin{document}

\newcommand{\FTdir}{}
\def\FTdir(#1,#2,#3){%
  \FTfile(#1,{{\color{black!40!white}\faFolderOpen}\hspace{0.2em}#3})
  (tmp.west)++(0.8em,-0.4em)node(#2){}
  (tmp.west)++(1.5em,0)
  ++(0,-1.3em) 
}

\newcommand{\FTfile}{}
\def\FTfile(#1,#2){%
  node(tmp){}
  (#1|-tmp)++(0.6em,0)
  node(tmp)[anchor=west,black]{\tt #2}
  (#1)|-(tmp.west)
  ++(0,-1.2em) 
}

\newcommand{\FTroot}{}
\def\FTroot{tmp.west}

\begin{tikzpicture}%
  \draw[color=black!60!white]
  \FTdir(\FTroot,root,/){       % root: parent = \FTroot
    \FTdir(root,etc,etc){       % normal dir: (parentID, currentID, label)
      \FTfile(etc,passwd)       % file:       (parentID, label)
      \FTdir(etc,init,init.d){
        \FTfile(init,cron)
      }
      \FTdir(etc,apt,apt){
        \FTfile(apt,sources.list)
        \FTfile(apt,preferences)
      }
    }

    ++(0,-0.5em)                % additional space if neded

    \FTdir(root,usr,usr) {
      \FTdir(usr,bin,bin){
        \FTfile(bin,gcc)
        \FTfile(bin,g++)
      }
      \FTdir(usr,share,share){
        \FTdir(share,doc,doc){
          \FTdir(doc,gcc,gcc){
            \FTfile(gcc,...)
          }
          \FTdir(doc,gcc,gcc-7){
            \FTfile(gcc,...)
          }
        }
      }
      \FTfile(usr,...)}

    ++(0,-0.5em)

    \FTdir(root,home,home){
      \FTdir(home,user,user)
    }
  };
  \end{tikzpicture}
\end{document}

image: preview of the tree

4
  • Very good answer. I upvote you.
    – Sebastiano
    Commented Mar 30, 2018 at 21:06
  • fantasitic. But it is not working if long filenames are used...
    – Harald
    Commented May 15, 2019 at 12:47
  • how do you do the little images?
    – voices
    Commented Nov 12, 2019 at 11:10
  • how can I change its font?
    – alper
    Commented Jul 24, 2022 at 0:03
9

Another example using the tikz trees library:

\documentclass{standalone}

\usepackage{tikz}
\usetikzlibrary{trees}

\usepackage{xcolor}

\begin{document}

\tikzstyle{every node}=[thick,anchor=west, rounded corners, font={\scriptsize\ttfamily}, inner sep=2.5pt]
\tikzstyle{selected}=[draw=blue,fill=blue!10]
\tikzstyle{root}=[selected, fill=blue!30]

\begin{tikzpicture}[%
    scale=.7,
    grow via three points={one child at (0.5,-0.65) and
    two children at (0.5,-0.65) and (0.5,-1.1)},
    edge from parent path={(\tikzparentnode.south) |- (\tikzchildnode.west)}]
  \node [root] {src}
    child { node [selected] {asf}
      child { node [selected] {avr32}
        child { node [selected] {drivers}
          child { node [selected] {canif}
            child { node {canif.h}}
            child { node {canif.c}}
          }
        }
      }
    }       
    child { node at (0,-3.5) [selected] {net}
      child { node [selected] {can}
        child { node {can.h}}
        child { node {can\_mob.h}}
        child { node {can\_mob.c}}
        child { node {can\_port.h}}
        child { node {can\_port.c}}
        child { node {can\_trcv.h}}
        child { node {can\_trcv.c}}
      }
    }
    child { node at (0,-8) {\dots}};
\end{tikzpicture}

\end{document}

folder structure

1
  • This looks very nice but doesn't handle placing siblings at asf, avr32, etc. when a sibling higher up has children. Commented Mar 20, 2016 at 16:06

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .