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.

Martin H
  • 18,164

4 Answers4

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

Jake
  • 232,450
  • 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 Jun 14 '11 at 08: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.

percusse
  • 157,807
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-\hline@row-1.north west) edge[\csname hline@row@\hline@row\endcsname] (\tikzlastnode-cell-\hline@row-\hline@cols.north 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}
Andrew Stacey
  • 153,724
  • 43
  • 389
  • 751
  • Ah, I love it when solutions to different problems play together like that. Very nice! – Jake Jun 20 '11 at 07:52
  • A good hint, thanks for investigating this further – Martin H Jun 20 '11 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 Jan 15 '17 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

F. Pantigny
  • 40,250
  • Saved my day! Thank you. – Wallflower Sep 20 '22 at 08: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 Sep 20 '22 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... – F. Pantigny Sep 20 '22 at 15:22