The following solution is a compromise between \nodepart{second} \hphantom{null}
(or an extra macro) and the condition that even not-empty parts have an specified width.
The style data+
takes one mandatory argument and sets the dimension of empty parts to the dimension of its argument.
In your example you would need data+=null
. Of course, if the actual content of the second part of the last node changes, you would have to change the argument given to data+
(though this could be a macro too).
Code
\documentclass{article}
\usepackage{tikz,calc}
\usetikzlibrary{shapes}
\tikzset{
data/.style={
draw,
rectangle split,
rectangle split parts=2,
text centered,
},
data+/.style={
data,
rectangle split every empty part={},% resets empty-part macro (explanation below)
rectangle split empty part width=\widthof{#1},
rectangle split empty part height=\heightof{#1},
rectangle split empty part depth=\depthof{#1},
},
}
\newcommand{\data}{data \nodepart{second} \phantom{null}}
\begin{document}
\begin{tikzpicture}[node distance=2cm]
\node [data+={null}] (A) {data};
\node [data, right of=A] (B) {\data};
\node [data, right of=B] (C) {data \nodepart{second} null};
\end{tikzpicture}
\end{document}
Output
Excursus: -1ex
The definition of the data+
style was previously
data+/.style={
data,
rectangle split empty part width=\widthof{#1}-1ex,
rectangle split empty part height=\heightof{#1},
rectangle split empty part depth=\depthof{#1},
},
The OP rightly asks
[W]hy do you have to subtract 1ex
from the width?
PGF/TikZ has one special macro that is inserted for an empty node-part if /pgf/rectangle split ignore empty parts
is false
(which seems to be the default contrary to the PGF manual), otherwise we wouldn’t even get a box.
The name of this macro is \pgf@lib@sh@rs@every@emptypart
(for short: empty-part macro).
One would expect that PGF stores away the lengths given to rectangle split empty part <width|height|depth>
keys and does not build this macro with the latest dimensions until it is needed. But this is not the case.
Every time rectangle split empty part <width|height|depth>
is used a rule of the given width, height or depth is created and added to the empty-part macro (all other length parameters of that rule are 0pt
, meaning they are invisible (e.g. \strut
)).
The problem arises that rules (even invisible ones) are getting stacked horizontally. Rules with a height or depth do not get stacked vertically so they don’t accumulate extra height/depth, but rules with a width (horizontal rules) accumulate.
But wait! We only used those keys once! Yes, but no.
As a matter of fact they are used once before, namely during the initialization.
The file pgflibraryshapes.multipart.code.tex
reads on lines 370, 378 and 386:
rectangle split empty part width=1ex,% line 370
rectangle split empty part height=1ex,% line 378
rectangle split empty part depth=0ex,% line 386
When we start we have a square box of 1ex × 1ex
. If we add a vertical rule of 2ex
we get a box of 1ex × 2ex
, but if we add a horizontal rule of 2ex
we get a box of 3ex × 1ex
.
A picture worth than thousand words?
I constructed an example, where
the initial box is empty:
\def\pgf@lib@sh@rs@every@emptypart{}% clean slate
and the rules have actually a “width” (orthogonal to its “length”), i.e. a horizontal rule has a height (actually a height and a depth so that the middle of the line lies on the baseline), and a vertical rule has a width.
Not-MWE
rs width
is a short-cut for rectangular split empty parts width
, the same holds for rs height
and rs depth
.
\node[rectangle split ignore empty parts=false] (a) {a};
\node[right of=a, rs width = 1ex] (b) {b};
\node[right of=b, rs height = 3ex] (c) {c};
\node[right of=c, rs depth = .5ex] (d) {d};
\node[below of=a, rs width = 1ex, rs width = \widthof{null}] [yshift=-.5cm] (e) {e};
\node[right of=e, rs height = 1ex, rs height = 2ex, rs height = .5ex] (f) {f};
\node[right of=f, rs depth = 1ex, rs depth = 2ex, rs depth = 3ex] (g) {g};
\node[right of=g, rs width = 1ex, rs depth = 2ex, rs height = 1ex, rs width = 2.5ex] (h) {h};
Output
The rules are:
- baseline: black
- horizontal (
width
): red
- vertical (
height
): green
- vertical (
depth
): blue
Is there a better solution?
Two come to mind:
instead of subtracting hard-coded 1ex
we could subtract the current actual width
rectangle split empty part width=\widthof{#1}-\widthof{\pgf@lib@sh@rs@every@emptypart}
which would work quite okay as long as its content is not wider than #1
.
Overwriting the empty-part macro.
There is a undocumented key rectangle split every empty part
that is not used once in PGF and TikZ. Its definition is
rectangle split every empty part/.store in=\pgf@lib@sh@rs@every@emptypart
We cannot do rectangle split every empty part={\phantom{null}}
because TikZ switched to a \nullfont
that swallows everything. (Have you ever tried to just write something in a tikzpicture
environment?). This is the same reason, I used calc
’s \<dimen>of
macros, they escape this cleverly.
We could fill this empty part macro with rules (not again!) overwriting any existing content, but then we could just use our old solution without -1ex
by just removing any content.
Code
\tikzset{
data+/.style={
data,
rectangle split every empty part={},% resets empty-part macro
rectangle split empty part width=\widthof{#1},
rectangle split empty part height=\heightof{#1},
rectangle split empty part depth=\depthof{#1},
},
}
I don't really get the question so I hope this is what you wanted. If you include a full document (such that we copy paste and see the problem on our systems) things are much more easier.
Here, you can change the default setting within a scope but your block
style had a node distance
which was resetting every time it is issued. I've made it 2mm such that we can see the difference easier.
\documentclass[tikz]{standalone}
\usetikzlibrary{arrows,shapes.geometric,positioning}
\begin{document}
\begin{tikzpicture}[decision/.style={diamond, draw, text width=4.5em, text badly centered, node distance=3.5cm, inner sep=0pt},
block/.style ={rectangle, draw, text width=6em, text centered, rounded corners, minimum height=4em, minimum height=2em},
cloud/.style ={draw, ellipse, minimum height=2em},
line/.style ={draw,-latex'},
node distance = 1cm,
auto]
\node [block] (1st) {1st};
\node [block, right= of 1st] (2nd1) {2nd1};
\begin{scope}[node distance=2mm and 10mm]%Here we change it for everything inside this scope
\node [block, above= of 2nd1] (2nd2) {2nd2};
\node [block, below= of 2nd1] (2nd3) {2nd3};
\node [block, right= of 2nd1] (3rd1) {3rd1};
\node [block, above= of 3rd1] (3rd2) {3rd2};
\node [block, above= of 3rd2] (3rd3) {3rd3};
\end{scope}
\node [block, below= of 3rd1] (3rd4) {3rd4};
\node [block, below= of 3rd4] (3rd5) {3rd5};
\path [line] (1st) -- (2nd1);
\path [line] (2nd1) -- (2nd2);
\path [line] (2nd1) -- (2nd3);
\path [line] (2nd2) -- (3rd3);
\path [line] (2nd1) -- (3rd1);
\path [line] (1st) -- (2nd1);
\end{tikzpicture}
\end{document}
Best Answer