Gantt – How to Create a Simple Gantt Chart with LaTeX

gantt

The Gantt chart latex features are really complicated. I just want a simple little thing like this, for visualizing scheduling algorithm

enter image description here

How should I do that?


ETA: I took the the code from here and added colors in.

\definecolor{p1}{HTML}{5780dc}
\definecolor{p2}{HTML}{dc0404}
\definecolor{p3}{HTML}{1ec81e}
\definecolor{p4}{HTML}{8424ac}
\definecolor{p5}{HTML}{e68142}

\begin{tikzpicture}[
    node distance = 0pt,
    G/.style = {draw, color=white, text width=2*#1mm, align=center,
                inner xsep=0pt, outer sep=0pt, 
                anchor=south west},
    tck/.style = {font=\scriptsize, below}
]
\newlength{\prev}
\foreach \n/\i [remember=\i as \j (initially 0), 
                evaluate=\i as \k using \i-\j] in {
                    1/2, 2/3, 3/11, 4/15, 5/20
                }
{
\node[G=\k, fill=p\n] at (\j/5,0) {$P_\n$};
    \node[tck] at (\i/5,0) {\i};
\ifnum\j=0
    \node[tck] at (0,0) {\j};
\fi
}
\end{tikzpicture}

This is mostly good, but what about one that's only 1 wide? It's too narrow, and the text gets cut off.

enter image description here

How can I alter it to avoid that? I want to make the whole thing wider, rather than making the text smaller.

Also, is there any way to have a sum variable? Something the number could be added to each time, so that the variable is the sum of the previous numbers? Something so that the input could be:

{p2/2, p1/1, p4/4, p5/5, p3/8}

Best Answer

This is a suggestion for a quite customisable simple gantt chart (maybe a bit overkill though). You can define a set of colors to cycle through, customise the width and height and the position of the ticks, and you can set a threshold that decides whether the label should be placed inside the node or as a pin. One current shortcoming is that the definition of colors cannot be scoped.

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

\newif\ifsimplegantttickpositionbelow
\tikzset{
    pics/simple gantt/.style={
        code={
            \ifsimplegantttickpositionbelow
                \path[/tikz/simple gantt/tick] (0,0) -- 
                    ++(0,{-1*\pgfkeysvalueof{/tikz/simple gantt/tick length}}) 
                    node[/tikz/simple gantt/tick label] {\pgfmathprintnumber{0}};
            \else
                \path[/tikz/simple gantt/tick] (0,\pgfkeysvalueof{/tikz/simple gantt/height}) -- 
                    ++(0,{\pgfkeysvalueof{/tikz/simple gantt/tick length}}) 
                    node[/tikz/simple gantt/tick label] {\pgfmathprintnumber{0}};
            \fi
            \foreach \n/\x [count=\i, remember=\x as \lastx (initially 0)] in {#1} {
                \ifsimplegantttickpositionbelow
                    \path[/tikz/simple gantt/tick] ({\x*\pgfkeysvalueof{/tikz/simple gantt/width unit}},0) -- 
                        ++(0,{-1*\pgfkeysvalueof{/tikz/simple gantt/tick length}}) 
                        node[/tikz/simple gantt/tick label] {\pgfmathprintnumber{\x}};
                \else
                    \path[/tikz/simple gantt/tick] ({\x*\pgfkeysvalueof{/tikz/simple gantt/width unit}},\pgfkeysvalueof{/tikz/simple gantt/height}) -- 
                        ++(0,{\pgfkeysvalueof{/tikz/simple gantt/tick length}}) 
                        node[/tikz/simple gantt/tick label] {\pgfmathprintnumber{\x}};
                \fi
                \pgfmathparse{int(mod(\i - 1, \pgfkeysvalueof{/tikz/simple gantt/color cycle length}) + 1)} 
                \global\pgfkeyslet{/tikz/simple gantt/color cycle step}{\pgfmathresult}
                \path[
                    /tikz/simple gantt/box, 
                    fill={simple gantt color \pgfkeysvalueof{/tikz/simple gantt/color cycle step}},
                ]
                    ({\lastx*\pgfkeysvalueof{/tikz/simple gantt/width unit}},0) rectangle 
                    ({\x*\pgfkeysvalueof{/tikz/simple gantt/width unit}},\pgfkeysvalueof{/tikz/simple gantt/height})
                    \pgfextra{\pgfmathparse{\x - \lastx}}
                    \ifdim\pgfmathresult pt < \pgfkeysvalueof{/tikz/simple gantt/label as pin if value below} pt\relax
                        node[/tikz/simple gantt/label, pin={[/tikz/simple gantt/label pin]\pgfkeysvalueof{/tikz/simple gantt/label pin angle}:\n}] {}
                    \else
                        node[/tikz/simple gantt/label] {\n}
                    \fi ;
            }
        }
    },
    simple gantt/color cycle length/.initial={0},
    simple gantt/color cycle step/.initial={1},
    simple gantt/color cycle/.code={
        \foreach \c [count=\i] in {#1} {
            \xglobal\colorlet{simple gantt color \i}{\c}
            \global\pgfkeyslet{/tikz/simple gantt/color cycle length}{\i}
        }
    },
    simple gantt/height/.initial={1cm},
    simple gantt/width unit/.initial={1cm},
    simple gantt/box/.style={},
    simple gantt/label/.style={pos=0.5},
    simple gantt/label pin/.style={above, pin edge={black, thin}, pin distance=0.5cm},
    simple gantt/label pin angle/.initial={90},
    simple gantt/label as pin if value below/.initial={1.5},
    simple gantt/tick/.style={draw},
    simple gantt/tick label/.style={below},
    simple gantt/tick position/.is choice,
    simple gantt/tick position/above/.code={\simplegantttickpositionbelowfalse},
    simple gantt/tick position/below/.code={\simplegantttickpositionbelowtrue},
    simple gantt/tick position={below},
    simple gantt/tick length/.initial={5pt},
    simple gantt/color cycle={blue!50, red!50, green!50},
}

\begin{document}

\begin{tikzpicture}

    \tikzset{
        simple gantt/.cd,
        width unit=0.33cm,
        box/.style={draw},
    }

    \pic at (0,0) {simple gantt={$P_1$/2, $P_2$/3, $P_3$/11, $P_4$/15, $P_5$/20}};

    \tikzset{
        simple gantt/.cd,
        height=0.75cm,
        color cycle={yellow, orange, cyan, magenta},
        label pin angle={270},
        label pin/.append style={below},
        tick position={above},
        tick label/.append style={above},
        label as pin if value below={4},
    }

    \pic at (0,-3) {simple gantt={A/1, B/3, C/9, D/10, E/20, F/25}};
    
\end{tikzpicture}

\end{document}

enter image description here


Scoping the colors is much easier using something like color cycle/.list={}. However, for proper cycling through the colors, I need the length of this list, which I find difficult to store properly. Maybe, it would be easier to define the color lists prior to the rest using IDs and for each pic reference to one of these lists using an ID.

Anyways, I found a way how to somehow scope the color lists, but I am quite sure that this is not the simplest and most straightforward way:

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

\newif\ifsimplegantttickpositionbelow
\newcounter{simpleganttcolorindex}
\tikzset{
    pics/simple gantt/.style={
        /tikz/simple gantt/color cycle get length/.expanded={\pgfkeysvalueof{/tikz/simple gantt/color cycle list}},
        code={
            \ifsimplegantttickpositionbelow
                \path[/tikz/simple gantt/tick] (0,0) -- 
                    ++(0,{-1*\pgfkeysvalueof{/tikz/simple gantt/tick length}}) 
                    node[/tikz/simple gantt/tick label] {\pgfmathprintnumber{0}};
            \else
                \path[/tikz/simple gantt/tick] (0,\pgfkeysvalueof{/tikz/simple gantt/height}) -- 
                    ++(0,{\pgfkeysvalueof{/tikz/simple gantt/tick length}}) 
                    node[/tikz/simple gantt/tick label] {\pgfmathprintnumber{0}};
            \fi
            \foreach \n/\x [count=\i, remember=\x as \lastx (initially 0)] in {#1} {
                \ifsimplegantttickpositionbelow
                    \path[/tikz/simple gantt/tick] ({\x*\pgfkeysvalueof{/tikz/simple gantt/width unit}},0) -- 
                        ++(0,{-1*\pgfkeysvalueof{/tikz/simple gantt/tick length}}) 
                        node[/tikz/simple gantt/tick label] {\pgfmathprintnumber{\x}};
                \else
                    \path[/tikz/simple gantt/tick] ({\x*\pgfkeysvalueof{/tikz/simple gantt/width unit}},\pgfkeysvalueof{/tikz/simple gantt/height}) -- 
                        ++(0,{\pgfkeysvalueof{/tikz/simple gantt/tick length}}) 
                        node[/tikz/simple gantt/tick label] {\pgfmathprintnumber{\x}};
                \fi
                \pgfmathparse{int(mod(\i - 1, \pgfkeysvalueof{/tikz/simple gantt/color cycle length}) + 1)} 
                \pgfkeyslet{/tikz/simple gantt/color cycle step}{\pgfmathresult}
                \path[
                    /tikz/simple gantt/box, 
                    fill={simple gantt color \pgfkeysvalueof{/tikz/simple gantt/color cycle step}},
                ]
                    ({\lastx*\pgfkeysvalueof{/tikz/simple gantt/width unit}},0) rectangle 
                    ({\x*\pgfkeysvalueof{/tikz/simple gantt/width unit}},\pgfkeysvalueof{/tikz/simple gantt/height})
                    \pgfextra{\pgfmathparse{\x - \lastx}}
                    \ifdim\pgfmathresult pt < \pgfkeysvalueof{/tikz/simple gantt/label as pin if value below} pt\relax
                        node[/tikz/simple gantt/label, pin={[/tikz/simple gantt/label pin]\pgfkeysvalueof{/tikz/simple gantt/label pin angle}:\n}] {}
                    \else
                        node[/tikz/simple gantt/label] {\n}
                    \fi ;
            }
        }
    },
    simple gantt/.cd,
    color cycle step/.initial={1},
    color cycle list/.initial={},
    color cycle length/.initial={1},
    color cycle loop/.code={
        \stepcounter{simpleganttcolorindex}
    },
    color cycle get length/.code={
        \setcounter{simpleganttcolorindex}{0}
        \pgfkeys{
            /tikz/simple gantt/color cycle loop/.list={#1},
            /tikz/simple gantt/color cycle length={\thesimpleganttcolorindex},
        }
    },
    color cycle define colors/.code={
        \stepcounter{simpleganttcolorindex}
        \colorlet{simple gantt color \thesimpleganttcolorindex}{#1}
    },
    color cycle/.code={
        \setcounter{simpleganttcolorindex}{0}
        \pgfkeys{
            /tikz/simple gantt/color cycle list={#1},
            /tikz/simple gantt/color cycle define colors/.list={#1},
        }
    },
    height/.initial={1cm},
    width unit/.initial={1cm},
    box/.style={},
    label/.style={pos=0.5},
    label pin/.style={above, pin edge={black, thin}, pin distance=0.5cm},
    label pin angle/.initial={90},
    label as pin if value below/.initial={1.5},
    tick/.style={draw},
    tick label/.style={below},
    tick position/.is choice,
    tick position/above/.code={\simplegantttickpositionbelowfalse},
    tick position/below/.code={\simplegantttickpositionbelowtrue},
    tick position={below},
    tick length/.initial={5pt},
    color cycle={blue!50, red!50, green!50},
}

\begin{document}

\begin{tikzpicture}

    \tikzset{
        simple gantt/.cd,
        width unit=0.33cm,
        box/.style={draw},
    }

    \pic at (0,0) {simple gantt={$P_1$/2, $P_2$/3, $P_3$/11, $P_4$/15, $P_5$/20}};

    \begin{scope}
    
    \tikzset{
        simple gantt/.cd,
        height=0.75cm,
        color cycle={yellow, orange, cyan, magenta},
        label pin angle={270},
        label pin/.append style={below},
        tick position={above},
        tick label/.append style={above},
        label as pin if value below={4},
    }

    \pic at (0,-3) {simple gantt={A/1, B/3, C/9, D/10, E/20, F/25}};

    \end{scope}
    
    \pic at (0,-6) {simple gantt={A/1, B/3, C/9, D/10, E/20, F/25}};

\end{tikzpicture}

\end{document}

enter image description here

Related Question