我最近决定正式学习 LaTeX,以便能够将自己的才能从“复制、粘贴和破解”TeX 用户提升为熟练使用这些工具的人。我正在尝试学习的一件事是使用 TikZ 绘制图形和图表。
因此,我尝试绘制三个表格结构,用箭头连接,使一个指向另一个。我将在这个问题的结论部分附上一张手绘草图,说明我的意图。
经过几个小时的挫折(当然,也对着电脑大喊大叫),我想出了以下 MWE,它产生了几乎一模一样我想要的是。
\documentclass{article}
\usepackage{tikz}
\usepackage{tabularx}
\usepackage{amsmath}
\usepackage{mathtools}
\usetikzlibrary{shapes}
\usetikzlibrary{arrows}
\usetikzlibrary{shadows}
\usetikzlibrary{positioning}
\begin{document}
\tikzstyle{box} = [
draw,
fill=white!7,
minimum width=5em,
text centered,
minimum height=2em,
drop shadow
]
\begin{tikzpicture}
\node (dir0) [box, minimum width=16em, minimum height=4em] {\vdots};
\node (dir1) [box, anchor=north, minimum width=16em] at
(dir0.south) {\texttt{Kernel Memory Pages}};
\node (dir2L) [box, anchor=north west, minimum width=8em] at
(dir1.south west) {\texttt{Permissions}};
\node (dir2R) [box, anchor=north east, minimum width=8em] at
(dir1.south east) {\texttt{Table Address}};
\node (dir3) [box, anchor=north, minimum width=16em] at
(dir2R.south west) {\texttt{User Memory Pages}};
\node (dir4L) [box, anchor=north west, minimum width=8em] at
(dir3.south west) {\texttt{Permissions}};
\node (dir4R) [box, anchor=north east, minimum width=8em] at
(dir3.south east) {\texttt{Table Address}};
\node (dir5) [box, anchor=north, minimum width=16em, minimum
height=4em] at (dir4R.south west) {\vdots};
\end{tikzpicture}
\end{document}
这会产生以下输出,这是我第一次使用 TikZ 绘图:
现在,虽然我对图形结果,我来自一个认为代码外观优雅是一种美德的学校,所以我开始用不同的风格重写上面的例子(比如此处“图表”下有记录的示例- “定义/声明/链接”样式),因为一旦我添加其他表,上面的代码必然会变得混乱,而且我担心如果以后需要调整它,它可能无法维护。
因此,我制作了以下表格,对此我非常满意。您可以看到,我还添加了其他两种类型的表格。
\documentclass{article}
\usepackage{tikz}
\usepackage{tabularx}
\usepackage{amsmath}
\usepackage{mathtools}
\usetikzlibrary{shapes}
\usetikzlibrary{arrows}
\usetikzlibrary{shadows}
\usetikzlibrary{positioning}
\begin{document}
\begin{tikzpicture} [
% ----- Node definitions ----- %
dirtablenode/.style = {
rectangle,
rectangle split,
rectangle split parts = 6,
fill = white!5,
minimum width = 5em,
minimum height = 2em,
text centered,
drop shadow
},
pagetablenode/.style = {
rectangle,
rectangle split,
rectangle split parts = 3,
fill = white!5,
minimum width = 5em,
minimum height = 2em,
text centered,
drop shadow
},
memmapnode/.style = {
rectangle,
rectangle split,
rectangle split parts = 4,
fill = white!5,
minimum width = 5em,
minimum height = 2em,
text centered,
drop shadow
}
]
% ----- Node constructions ----- %
\node [dirtablenode] (directory) {
\nodepart [minimum width = 16em, minimum height = 4em] {one}
\vdots
\nodepart [minimum width = 16em, minimum height = 2em] {two}
\texttt{Kernel Memory Pages}
\nodepart [minimum width = 8em, minimum height = 2em] {three}
\texttt{Permissions}
\nodepart [minimum width = 8em, minimum height = 2em] {four}
\texttt{Table Address}
\nodepart [minimum width = 16em, minimum height = 2em] {five}
\texttt{User Memory Pages}
\nodepart [minimum width = 8em, minimum height = 2em] {six}
\texttt{Permissions}
\nodepart [minimum width = 8em, minimum height = 2em] {seven}
\texttt{Table Address}
\nodepart [minimum width = 16em, minimum height = 4em] {eight}
\vdots
};
\node [pagetablenode] (kpagetable) [right=of directory] {
\nodepart [minimum width = 16em] {kbase} \texttt{Base Address}
\nodepart [minimum width = 16em] {klimit} \texttt{Address Limit}
\nodepart [minimum width = 16em] {kperms} \texttt{Permissions}
};
\node [pagetablenode] (upagetable) [below=of kpagetable] {
\nodepart [minimum width = 16em] {ubase} \texttt{Base Address}
\nodepart [minimum width = 16em] {ulimit} \texttt{Address Limit}
\nodepart [minimum width = 16em] {uperms} \texttt{Permissions}
};
\node [memmapnode] (sysmemory) [below=of directory] {
\nodepart [minimum width = 16em, minimum height = 4em] {one} \vdots
\nodepart [minimum width = 16em, minimum height = 4em] {kernel}
\texttt{Kernel \\ Memory \\ (Protected)}
\nodepart [minimum width = 16em, minimum height = 4em] {user}
\texttt{User \\ Memory \\ (Unprivileged)}
\nodepart [minimum width = 16em, minimum height = 4em] {one} \vdots
};
% ----- Connection lines ----- %
% ??
\end{tikzpicture}
\end{document}
为了将所有内容放在一起并更好地表明我正在尝试做的事情(而不是坐在这里假设 TeX.SE 上的人们能读懂我的想法),这里是我设想的整个外观的涂鸦tikzpicture
:
因此,以下是问题的部分内容,我已将其分解。我尝试在这里搜索答案,但找不到任何可以解决这个问题的内容,而且对于初学者来说,浏览 TikZ 手册是令人生畏的(至少可以这么说)。
是否
\nodepart
支持与手动创建的节点对齐相同的对齐方式?您会注意到,我声明第一个 (directory
节点有 6 个部分,但实际上它有 8 个节点部分。我可以使用相同的anchor
和选项at <reference>
内的\nodepart
选项来对齐它们吗?\node
对齐(即)是否[right=of directory]
接受多个对齐约束,如果是,我该如何对齐它,以使相应实例的顶部和底部pagetablenode
与目录正确对齐?我该如何对齐底部表格(
sysmemory
),使其位于两个上方表格的中心,并且(理想情况下)使用半球形线条将右上方表格与其连接起来(如图所示)?
简而言之,我需要对第二个更有序的 MWE 进行哪些更改才能实现我所设想的图表?请记住,您可能需要为我稍微分解一下 - 我对 TikZ 一窍不通。我对如何答案是正确的,因为我的实际答案是这样的。
答案1
正如 Zarko 建议的那样,您应该阅读rectangle split
信息(第 726 至 728 页)和positioning
图书馆信息。
从一开始,您将了解到,minimum width
对于垂直分割的矩形有效,但minimum height
会被忽略。然后,每个节点部分中的所有这些选项都可以被抑制。矩形分割宽度将由内部部分宽度的最大值或固定minimum width
。如果您想修复某个特定值,minimum width
您可以在选项中进行操作,node
如下所示kpagetable
。
当minimum height
某些节点部分需要某些工具时,我们需要其他工具。一个可能的工具可能是parbox
(参见[Yiannis 的回答](https://tex.stackexchange.com/a/10273/1952 您还需要阅读)以了解所有参数),但也可以使用0 宽度规则\rule{0pt}{...}
或文本。phantom
阅读有关positioning
图书馆的信息,你会明白
right= 3cm of directory.north east, anchor=north west
将强制将相应节点放置在距节点右上角 3 厘米的位置directory
并与其顶线对齐。
可以使用类似的结构将其他节点与底角对齐。
aux
最后定义了一个节点来放置sysmemory
节点。当然,还有其他构造方法可以得到类似的结果。
您的代码已进行了一些进一步的清理。首先,由于所有块仅在其部分上有所不同,因此仅定义了一种以拆分部分为参数的样式。如果需要,minimum width
可以将选项添加到每个特定节点声明中。
第二,不再需要\texttt{...}
为每个文本部分指定选项,而是font
使用选项。这可以节省一些输入。
最后一条评论,由于无法水平划分部件,因此使用两个部件框和手动绘制的划分来表示包含permissions
和的线table address
。
\documentclass{article}
\usepackage{tikz}
\usetikzlibrary{shapes}
\usetikzlibrary{arrows}
\usetikzlibrary{shadows}
\usetikzlibrary{positioning}
\begin{document}
\begin{tikzpicture} [
% ----- Node definitions ----- %
dirtablenode/.style = {
draw,
rectangle,
rectangle split,
rectangle split parts = #1,
fill = white!5,
align=center,
drop shadow,
font=\ttfamily
},
]
% ----- Node constructions ----- %
\node [dirtablenode=6] (directory) {
\nodepart{one}
\parbox[t][15mm][c]{1cm}{\vdots}
\nodepart[]{two}
Kernel Memory Pages
\nodepart{three}
\parbox[t][][c]{3cm}{\centering Permissions}\parbox[t][][c]{3cm}{\centering Table Address}
\nodepart{four}
User Memory Pages
\nodepart{five}
\parbox[t][][c]{3cm}{\centering Permissions}\parbox[t][][c]{3cm}{\centering Table Address}
\nodepart{six}
\parbox[t][15mm][c]{1cm}{\vdots}
};
\draw (directory.two split)--(directory.three split);
\draw (directory.four split)--(directory.five split);
\node [dirtablenode=3, minimum width=5cm, right= 3cm of directory.north east, anchor=north west] (kpagetable) {
\nodepart{one} Base Address
\nodepart{two} Address Limit
\nodepart{three} Permissions
};
\node [dirtablenode=3, right=3cm of directory.south east, anchor=south west] (upagetable) {
\nodepart{one} Base Address
\nodepart{two} Address Limit
\nodepart{three} Permissions
};
\path (directory.south east) -- (upagetable.south west) coordinate[midway] (aux);
\node [dirtablenode=4, below=of aux, anchor=north](sysmemory) {
\nodepart{one} \vdots
\nodepart{two}
Kernel \\ Memory \\ (Protected)
\nodepart{three}
User \\ Memory \\ (Unprivileged)
\nodepart{four} \vdots
};
% ----- Connection lines ----- %
\draw[rounded corners,->] (directory.three east)--++(0:1cm)--([xshift=-1cm]kpagetable.north west)--(kpagetable.north west);
\draw[rounded corners,->] (directory.five east)--++(0:1cm)--([xshift=-1cm]upagetable.north west)--(upagetable.north west);
\draw[->] (kpagetable.two east) to[out=-10,in=5] (sysmemory.two east);
\draw[->] (upagetable.two east) to[out=-10,in=5] (sysmemory.three east);
\end{tikzpicture}
\end{document}
答案2
...我改变了主意...过了一会儿,我再次将我的解决方案(基于 Ignasi 第一个解决方案)与 Ignasi 解决方案进行了比较,我估计差异很小,但不可忽略。所以,这是我的解决方案:
\documentclass{article}
\usepackage{tabularx}
\newcolumntype{C}{>{\centering\arraybackslash}X}
\usepackage{tikz}
\usetikzlibrary{arrows.meta,
positioning,
shadows,shapes}
\newcommand{\myvdots}{\vdots\rule[-2ex]{0ex}{5ex}}
\begin{document}
\begin{tikzpicture} [% TikZ presets
> = {Straight Barb[length=5pt]},
node distance = 0mm and 24mm,
dirtablenode/.style args = {#1/#2}{%
rectangle split,
rectangle split parts = #1,
draw,
fill=white,
inner sep=2mm,
font=\ttfamily,
text width=#2,
align=center,
drop shadow,
},
]
% ----- Node constructions ----- %
\node (directory) [dirtablenode=6/16em]
{
\nodepart{one} \myvdots
\nodepart{two} Kernel Memory Pages
\nodepart{three} \begin{tabularx}{\hsize}{C C}
Permissions & Table Address
\end{tabularx}
\nodepart{four} User Memory Pages
\nodepart{five} \begin{tabularx}{\hsize}{C C}
Permissions & Table Address
\end{tabularx}
\nodepart{six} \myvdots
};
\draw (directory.two split) -- (directory.three split)
(directory.four split) -- (directory.five split);
%
\node (kpagetable) [dirtablenode=3/12em,
below right= of directory.north east]
{
\nodepart{one} Base Address
\nodepart{two} Address Limit
\nodepart{three} Permissions
};
%
\node (upagetable) [dirtablenode=3/12em,
above right=of directory.south east]
{
\nodepart{one} Base Address
\nodepart{two} Address Limit
\nodepart{three} Permissions
};
\path (directory.south east)-- coordinate (mid) (upagetable.south west);
\node (sysmemory) [dirtablenode=4/12em,
below=12mm of mid]
{
\nodepart{one} \myvdots
\nodepart{two} Kernel Memory (Protected)
\nodepart{three} User Memory (Unprivileged)
\nodepart{four} \myvdots
};
% ----- Connection lines ----- %
\draw[rounded corners,->]
(directory.three east) --++ (0:1cm) -- ([xshift=-1cm]kpagetable.north west) -- (kpagetable.north west);
\draw[rounded corners,->]
(directory.five east) --++ (0:1cm) -- ([xshift=-1cm]upagetable.north west) -- (upagetable.north west);
\draw[->] (kpagetable.two east) to[out=-30,in=0,looseness=1.2] (sysmemory.two east);
\draw[->] (upagetable.two east) to[out=-30,in=0,looseness=1.5] (sysmemory.three east);
\end{tikzpicture}
\end{document}
主要区别:对于垂直点,我定义了新命令,它为点添加了更多垂直空间,multipart
节点有两个参数,一个用于部分数,第二个用于节点宽度(它由决定\text width
,因此较长的文本可以自动分成更多行),对于箭头,我使用arrows.meta
并将它们放大,这样更清晰可见,环更大,更接近对称。我还删除了所有不必要的选项(在我的评论中提到)和包。结果略有不同: