How to connect Beamer blocks by arrows?
JLDiaz explained you how to fix your code, but the result could be improved. The solution I suggest perhaps is not perfect because is something that requires a bit of care, but provides a better result IMHO.
It is based on the package textpos
to position the blocks, on Changing default width of blocks in beamer to customize the block width and on the \tikzmark
macro to identify blocks.
Actually, what is interesting in your solution, is the mechanism in which you identified blocks putting them inside a TikZ node for later connect them using the node's anchors. This solution, however, uses textpos
thus: how to identify the whole block? That's the reason why \tikzmark
is needed. Notice that using textpos
helps in not having such problems of vertical misalignment.
The \tikzmark
is built in such a way that the node could be extended in its width and in its height (that's the part that requires a bit of care): notice that the height of the block ultimately depends on your content, so my suggestion is 1) first write the content 2) fix its height later.
The point of the mark
identified is exactly in the center of the block title and the node identifies an area larger than the block:
As you can see from the picture, half of the node built via \tikzmark
is empty: this is done to ensure that connecting parallel blocks the arrow could be realized by means of something like
\path[line width=0.2cm](n2.east)edge[bend left](n1.west);
As a consequence, the upper part remains empty and you should be aware of this behaviour in case you draw connections starting/ending on the top of the block. This problem has been fixed in a particular way with a special anchor.
The code showing all stuff:
\documentclass[aspectratio=169]{beamer}
\usepackage{lmodern}
\usepackage{tikz}
\usepackage[overlay]{textpos}
\usetheme{Frankfurt}
\newcommand{\tikzmark}[2][minimum width=6cm,minimum height=1.5cm]{
\tikz[remember picture,overlay]
\node[anchor=west,
inner sep=0pt,
outer sep=6pt,
xshift=-0.5em,
yshift=-3ex,
#1](#2){};
}
\newcommand{\shownode}[1]{
\tikz[remember picture,overlay]\draw[red](#1.south east)rectangle(#1.north west);
}
\newcommand{\showanchor}[1]{
\tikz[remember picture,overlay]\draw[red,thick,mark=x] plot coordinates{(#1)};
}
% original code from Stefan Kottwitz
% https://tex.stackexchange.com/a/12551/13304
\newenvironment<>{varblock}[2][.9\textwidth]{%
\setlength{\textwidth}{#1}
\begin{actionenv}#3%
\def\insertblocktitle{#2}%
\par%
\usebeamertemplate{block begin}%
}
{\par%
\usebeamertemplate{block end}%
\end{actionenv}}
\newenvironment{myblock}[1]{\begin{textblock*}{500pt}(#1)}{\end{textblock*}}
% special way to reach the top of the block
\def\newabove(#1){
([yshift=1.5ex]#1.center)
}
\begin{document}
\begin{frame}
\begin{myblock}{0.55\textwidth, -0.2\textheight}
\tikzmark{n1}
\shownode{n1}
%remove the previous line: just used to see where the invisible node is placed
\begin{varblock}[6cm]{title1}
text1
\end{varblock}
\showanchor{n1}
%remove also this: just used to see where n1 is positioned
\end{myblock}
\begin{myblock}{0em, -0.2\textheight}
\tikzmark{n2}
\shownode{n2}
\begin{varblock}[6cm]{title2}
text2
\end{varblock}
\showanchor{n2}
\end{myblock}
\begin{myblock}{0.3\textwidth, 0.2\textheight}
\tikzmark[minimum width=5cm,minimum height=2.25cm]{n3} % customization of width and height
\shownode{n3}
\begin{varblock}[5cm]{title3}
text3\\
text
\end{varblock}
\showanchor{n3}
\end{myblock}
\begin{tikzpicture}[remember picture,overlay,-stealth]
\path[line width=0.2cm](n2.east)edge[bend left](n1.west);
\path[line width=0.2cm](n1.south)edge[bend left](n3.east);
\path[line width=0.2cm](n3.west)edge[bend left](n2.south);
% in case you need to connect some block from ``north''
%\path[line width=0.2cm](n3.north)edge[bend left](n1.south west); % is wrong
\path[line width=0.2cm]\newabove(n3) edge[bend left](n1.south west);
\end{tikzpicture}
\end{frame}
\end{document}
Removing all stuff to make visible nodes and anchors and the special path showing how to reach the top of the block, you end up with:
which I guess is what you wanted.
How to deal with the height problem of blocks
Since this could be a cons of the procedure, let's have an example.
At first one should care about the content of the blocks without considering height or other stuff. For instance:
\documentclass[aspectratio=169]{beamer}
\usepackage{lmodern}
\usepackage{tikz}
\usetheme{Frankfurt}
\usepackage[overlay]{textpos}
\newcommand{\tikzmark}[2][minimum width=6cm,minimum height=1.5cm]{
\tikz[remember picture,overlay]
\node[anchor=west,
inner sep=0pt,
outer sep=6pt,
xshift=-0.5em,
yshift=-3ex,
#1](#2){};
}
\newcommand{\shownode}[1]{
\tikz[remember picture,overlay]\draw[red](#1.south east)rectangle(#1.north west);
}
\newcommand{\showanchor}[1]{
\tikz[remember picture,overlay]\draw[red,thick,mark=x] plot coordinates{(#1)};
}
% original code from Stefan Kottwitz
% https://tex.stackexchange.com/a/12551/13304
\newenvironment<>{varblock}[2][.9\textwidth]{%
\setlength{\textwidth}{#1}
\begin{actionenv}#3%
\def\insertblocktitle{#2}%
\par%
\usebeamertemplate{block begin}%
}
{\par%
\usebeamertemplate{block end}%
\end{actionenv}}
\newenvironment{myblock}[1]{\begin{textblock*}{500pt}(#1)}{\end{textblock*}}
% special way to reach the top of the block
\def\newabove(#1){
([yshift=1.5ex]#1.center)
}
\begin{document}
\begin{frame}
\begin{myblock}{0cm,-2.5cm}
\tikzmark{n1}
\begin{varblock}[6cm]{title1}
Hello! :) This is the text of my block. Here I want to say
that the height of the blocks is not so problematic.
\end{varblock}
\end{myblock}
\begin{myblock}{8cm,-2.5cm}
\tikzmark{n2}
\begin{varblock}[6cm]{title2}
This is my very long block with even an equation:
\[\Delta_n=
\begin{cases}
1 \quad q_n>0\\
0 \quad q_n=0
\end{cases}
\]
\end{varblock}
\end{myblock}
% I can leave all blank lines I want
\begin{myblock}{8cm,2cm}
\tikzmark{n3}
\begin{varblock}[6cm]{title3}
Short text
\end{varblock}
\end{myblock}
\begin{myblock}{0cm,2cm}
\tikzmark{n4}
\begin{varblock}[6cm]{title4}
Short text
\end{varblock}
\end{myblock}
\begin{tikzpicture}[remember picture,overlay,-stealth,line width=0.2cm]
\draw (n1.east)--(n2.west);
\draw (n2.south)--\newabove(n3);
\draw (n3.west)--(n4.east);
\draw \newabove(n4)--(n1.south);
\end{tikzpicture}
\end{frame}
\end{document}
Notice that I intentionally left some blank lines. After two compilation runs we get:
Oh.. there's a problem with blocks 1 and 2. Why? Because the height is wrong; using our \shownode
and \showanchor
it is possible to understand the reason:
Notice now that the default value is 1.5cm
for the height. Thus something like 0.75cm
for a block with the title and one line in the body.
In the block 1 we have 4 lines+the title, so roughly speaking, we have three times the standard height: 0.75cm x 3=2.25cm
. This however is just the block so to have the whole node we should multiply by two again. Totally: 4.5cm
.
Look now at the second block: it is just a bit higher that block 1. I guess that's 1cm
higher, so let's put 5.5cm
.
So we have to update:
\begin{myblock}{0cm,-2.5cm}
\tikzmark[minimum width=6cm, minimum height=4.5cm]{n1}
\shownode{n1}
\begin{varblock}[6cm]{title1}
Hello! :) This is the text of my block. Here I want to say
that the height of the blocks is not so problematic.
\end{varblock}
\showanchor{n1}
\end{myblock}
\begin{myblock}{8cm,-2.5cm}
\tikzmark[minimum width=6cm, minimum height=5.5cm]{n2}
\shownode{n2}
\begin{varblock}[6cm]{title2}
This is my very long block with even an equation:
\[\Delta_n=
\begin{cases}
1 \quad q_n>0\\
0 \quad q_n=0
\end{cases}
\]
\end{varblock}
\showanchor{n2}
\end{myblock}
This provides:
More or less we're done. But I'm still unsatisfied with block 2: it's not very precise. I guess we need 0.25cm
more. So:
\begin{myblock}{8cm,-2.5cm}
\tikzmark[minimum width=6cm, minimum height=5.75cm]{n2}
\shownode{n2}
\begin{varblock}[6cm]{title2}
This is my very long block with even an equation:
\[\Delta_n=
\begin{cases}
1 \quad q_n>0\\
0 \quad q_n=0
\end{cases}
\]
\end{varblock}
\showanchor{n2}
\end{myblock}
This gives:
Oh.. bad. Too much! Ok, let's shorten it a bit to 5.7cm
. This will be for sure right. In conclusion:
I fixed your code at some points:
- The most important, you have to give also the
remember picture
option to your overlays. Without it, the name of each node does not refer to the proper coordinates, since they are nodes in a different tikz picture. baseline
option is not really required in this case.inner sep
set to zero for the block nodes%
added at the end of some lines to prevent vertical space (which still happens, I don't know why, and can be made visible by uncommenting the first line of theevery overlay node
style.
The resulting code is:
\documentclass[aspectratio=169]{beamer}
\usepackage{tikz}
\usetheme{Frankfurt}
\tikzset{
every overlay node/.style={
%draw=black,fill=white,rounded corners,
anchor=north west, inner sep=0pt,
},
}
% Usage:
% \tikzoverlay at (-1cm,-5cm) {content};
% or
% \tikzoverlay[text width=5cm] at (-1cm,-5cm) {content};
\def\tikzoverlay{%
\tikz[remember picture, overlay]\node[every overlay node]
}%
\begin{document}
\begin{frame}
\tikzoverlay (n1) at (8cm,2cm) {%
\begin{minipage}{0.4\textwidth}%
\begin{block}{title1}%
{Text1}
\end{block}
\end{minipage}
};
\tikzoverlay (n2) at (0cm,2cm) {%
\begin{minipage}{0.4\textwidth}%
\begin{block}{title2}%
Text2
\end{block}
\end{minipage}
};
\tikzoverlay (n3) at (5cm,0cm) {%
\begin{minipage}{0.4\textwidth}%
\begin{block}{Title3}%
Text3
\end{block}
\end{minipage}
};
\begin{tikzpicture}[remember picture, overlay]
\path [line width=0.3cm,->] (n1.south) edge (n3.south);
\path [line width=0.3cm,->] (n3.north) edge (n2.south);
\path [line width=0.3cm,->] (n2.east) edge (n1.west);
\end{tikzpicture}
\end{frame}
\end{document}
Which produces (after compiling twice):
Which is ugly, IMHO, but I guess it is what you was asking for.
Edit:
When I wrote the above code, I didn't pay attention to the values of the coordinates where each node was drawn. I assumed that the result with node2 at different level than node1 was intentional. But the OP comment made me realize that indeed both nodes use the same y-coordinate, so, why they are not aligned?
The reason is that they are not drawn in the same tikzpicture
, so they don't share the space of coordinates. Each tikzpicture
(or \tikz
command) starts its own coordinate system. So the new question is "why then it almost worked?".
The reason for this is that each figure has the option overlay
, which means that independently of what the picture actually draws, the space reserved by tex for that picture is zero. It is a dimensionless point. That point is then the origin of the figure.
So if we had three figures without space inbetween, since each is seen by tex as a dimensionless point, the three will be drawn "at the same point", and thus their coordinate systems will match. But if the figures are separated by words, then each one will have its own origin.
It happens that in your case the figures are separated by blank spaces and even by `\par, because there are empty lines in the source code, and end-of-lines between figures.
The solution is then to remove all this unwanted space, by removing blank lines between figures and commenting-out the carriage returns at the end of each figure (which would otherwise produce a space):
\documentclass[aspectratio=169]{beamer}
\usepackage{tikz}
\usetheme{Frankfurt}
\tikzset{
every overlay node/.style={
%draw=black,fill=white,rounded corners,
anchor=north west, inner sep=0pt,
},
}
% Usage:
% \tikzoverlay at (-1cm,-5cm) {content};
% or
% \tikzoverlay[text width=5cm] at (-1cm,-5cm) {content};
\def\tikzoverlay{%
\tikz[remember picture, overlay]\node[every overlay node]
}%
\begin{document}
\begin{frame}
\tikzoverlay (n1) at (8cm,2cm) {%
\begin{minipage}{0.4\textwidth}%
\begin{block}{title1}%
{Text1}
\end{block}
\end{minipage}
};%
\tikzoverlay (n2) at (0cm,2cm) {%
\begin{minipage}{0.4\textwidth}%
\begin{block}{title2}%
Text2
\end{block}
\end{minipage}
};%
\tikzoverlay (n3) at (5cm,0cm) {%
\begin{minipage}{0.4\textwidth}%
\begin{block}{Title3}%
Text3
\end{block}
\end{minipage}
};%
%
\begin{tikzpicture}[remember picture, overlay]
\path [line width=0.3cm,->] (n1.south) edge (n3.south);
\path [line width=0.3cm,->] (n3.north) edge (n2.south);
\path [line width=0.3cm,->] (n2.east) edge (n1.west);
\end{tikzpicture}
\end{frame}
\end{document}
Result: