20

In this question tikz-matrix-as-a-replacement-for-tabular a nice solution was given to let the matrix in tikz behave like tabular.

In this solution cell/.style={rectangle} was used to draw lines around every cell. But usually, however, I'd rather not give my tables this "grid"-look.

Is there a way within the tikz matrix to draw horizontal lines between rows that essentially behave like \hline within tabular?

I know I could put a node in the first and final cell of a row and draw a line between them, but this is not really handy compared to a single command like \hline and not very automated either.

4 Answers 4

17

You can use a style that uses execute at end cell to draw a horizontal line at the top or bottom edge of selected cells. By applying this style to complete using row <number>/.style, you can get an effect similar to \hline:

\documentclass{article}
\usepackage{tikz}
\usetikzlibrary{matrix}
\begin{document}

\tikzset{toprule/.style={%
        execute at end cell={%
            \draw [line cap=rect,#1] (\tikzmatrixname-\the\pgfmatrixcurrentrow-\the\pgfmatrixcurrentcolumn.north west) -- (\tikzmatrixname-\the\pgfmatrixcurrentrow-\the\pgfmatrixcurrentcolumn.north east);%
        }
    },
    bottomrule/.style={%
        execute at end cell={%
            \draw [line cap=rect,#1] (\tikzmatrixname-\the\pgfmatrixcurrentrow-\the\pgfmatrixcurrentcolumn.south west) -- (\tikzmatrixname-\the\pgfmatrixcurrentrow-\the\pgfmatrixcurrentcolumn.south east);%
        }
    }
}

\begin{tikzpicture}
\matrix [matrix of nodes,
    row sep=-\pgflinewidth,
    column sep=-\pgflinewidth,
    nodes={rectangle,minimum width=3em,outer sep=0pt},
    row 1/.style={toprule=thick,bottomrule},
    row 3/.style={bottomrule=thick}]
{
0   & 6 & 5\\
1   & 3 & 7\\
21 & 22 & 23\\
};
\end{tikzpicture}
\end{document}

matrix of cells with horizontal lines

1
  • 1
    A very nice solution. I like having the option to specify the line width. This gives the whole thing almost the feel of booktabs.
    – Martin H
    Commented Jun 14, 2011 at 8:38
8

The following might at least decrease the burden of putting the nodes etc. (I hope). In fact, it is similar to what you have already ruled out as a bad solution, but still it sweeps the ugliness under the rug and does not introduce new nodes other than the tikz matrix has already defined.

\documentclass{article}
\usepackage{tikz}
\usetikzlibrary{matrix}
\newcommand{\hhlline}[3]{\draw (#1-#2-1.south west) -- (#1-#2-#3.south east);}
\begin{document}
\begin{tikzpicture}
\node[matrix of math nodes] (mymat) {
0   &a  & 6 \\
1   &b  & 3 \\
2   &c  & 9 \\
};
\hhlline{mymat}{1}{2};
\hhlline{mymat}{2}{3};
\end{tikzpicture}
\end{document}

enter image description here

So, you use the command \hhlline with three arguments, the name of the matrix, the row you want to draw the line under it and the number of columns you want to keep drawing.

I am pretty sure that this can further be improved but I am not very good at accessing the internal variables of the matrix environment and using it inside the command automatically. Anyway, hope it helps. Cheers

And last minute addition: Jake's solution looks quite nice.

8

There is an issue with Jake's code if the nodes are different sizes. This can be fixed using the matrixcells part of the TeX-SX TikZ package (currently available from launchpad) to put the lines in after the matrix has been drawn and positioned correctly. Here's a sample of the code, including an example showing where Jake's code breaks down.

Result:

hlines and tikz matrices

Code:

\documentclass{standalone}
\usepackage{tikz}
\usetikzlibrary{matrix}
\usepackage{matrixcells}
\begin{document}


\tikzset{toprule/.style={%
        execute at end cell={%
            \draw [line cap=rect,#1] (\tikzmatrixname-\the\pgfmatrixcurrentrow-\the\pgfmatrixcurrentcolumn.north west) -- (\tikzmatrixname-\the\pgfmatrixcurrentrow-\the\pgfmatrixcurrentcolumn.north east);%
        }
    },
    bottomrule/.style={%
        execute at end cell={%
            \draw [line cap=rect,#1] (\tikzmatrixname-\the\pgfmatrixcurrentrow-\the\pgfmatrixcurrentcolumn.south west) -- (\tikzmatrixname-\the\pgfmatrixcurrentrow-\the\pgfmatrixcurrentcolumn.south east);%
        }
    },
}

\makeatletter
\tikzset{
    hlines/.style={%
      label cells,
      initialise hlines,
      append after command={%
        \pgfextra{\pgfmathtruncatemacro{\hline@cols}{\pgfmatrixcurrentcolumn - 1}}%
        \ifx\hline@rows\pgfutil@empty
        \else
        \foreach \hline@row in \hline@rows {
          \pgfextra{\edef\hline@temp{
          (\tikzlastnode-cell-\[email protected] west) edge[\csname hline@row@\hline@row\endcsname] (\tikzlastnode-cell-\hline@row-\[email protected] east)}}
          \hline@temp
        }
        \fi
      }
    },
    initialise hlines/.code={
      \global\let\hline@rows=\pgfutil@empty
      \let\hline=\hline@inmatrix
    }
}

\newcommand\hline@inmatrix[1][]{%
    \ifx\hline@rows\pgfutil@empty
     \xdef\hline@rows{\the\pgfmatrixcurrentrow}%
    \else
     \xdef\hline@rows{\hline@rows,\the\pgfmatrixcurrentrow}%
    \fi
    \expandafter\xdef\csname hline@row@\the\pgfmatrixcurrentrow\endcsname{#1}%
}
\makeatother

\begin{tikzpicture}
\matrix [matrix of nodes,
    row sep=-\pgflinewidth,
    column sep=-\pgflinewidth,
    nodes={rectangle,minimum width=3em,outer sep=0pt},
    row 1/.style={toprule=thick,bottomrule},
    row 3/.style={bottomrule=thick}] (a)
{
\(\frac12\)   & 6 & 5\\
1   & 3 & 7\\
21 & 22 & 23\\
};
\matrix [matrix of nodes,hlines,anchor=north] (mm) at (a.south)
{
\(\frac12\)   & 6 & 5\\ \hline[thick]%
1   & 3 & 7\\ \hline
21 & 22 & 23\\
};
\end{tikzpicture}
\end{document}
3
  • Ah, I love it when solutions to different problems play together like that. Very nice!
    – Jake
    Commented Jun 20, 2011 at 7:52
  • A good hint, thanks for investigating this further
    – Martin H
    Commented Jun 20, 2011 at 10:43
  • I can't reproduce this code. I get an error stating that Command \hline@inmatrix already defined., and the two lines are striking through rows 2 and 3, rather than sitting in between.
    – Karalga
    Commented Jan 15, 2017 at 13:23
1

For information, the environment {NiceTabular} of nicematrix creates a tabular similar to the classical {tabular} (of array) but creates a PGF/Tikz nodes under the content of each cell (and creates also PGF/Tikz nodes de type coordinate corresponding to the position of the potential rules).

Thus, it's possible to add rules with the technics of the classical {tabular} and access each cell as a PGF/Tikz node.

\documentclass{article}
\usepackage{nicematrix,booktabs,tikz}

\begin{document}

\begin{NiceTabular}{ccc}[name=A]
\toprule
one & two & three \\
\midrule
four & five & six \\
seven & eight & nine \\
\bottomrule
\end{NiceTabular}
\hspace{2cm}
{\renewcommand{\arraystretch}{1.4}
\begin{NiceTabular}{ccc}[hvlines,name=B]
one & two & three \\
four & five & six \\
seven & eight & nine 
\end{NiceTabular}}

\begin{tikzpicture}[remember picture, overlay]
\draw [red,thick,->] (A-2-2) to [bend left] (B-2-2) ; 
\end{tikzpicture}

\end{document}

You need several compilations because of the PGF/Tiks nodes.

Output of the above code

3
  • Saved my day! Thank you.
    – Wallflower
    Commented Sep 20, 2022 at 8:11
  • However, I have a small problem when it comes to the caption. I have insert my tikzpicture (who overlayes a given NiceTabular) inside a table environment to have a 'Table' caption. The latter is printed in the page previous to the one on which the table is...
    – Wallflower
    Commented Sep 20, 2022 at 12:41
  • @Wallflower: You should present your problem in a dedicated question on TeX Stack Exchange. I fear that it will not possible to give you a good answer in the comments... Commented Sep 20, 2022 at 15:22

You must log in to answer this question.

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