如何从 pgfkeys 样式元素中提取值

如何从 pgfkeys 样式元素中提取值

可以说我已经定义了一些风格:

\tikzset{mystyle/.style={inner xsep=2pt, outer xsep=10pt}}

给定样式的名称:我以后如何获取其某个元素的值(可能被覆盖),例如outer xsep

我目前的尝试是这样的:

\newcommand{\Xouterxsep}{%
  \bgroup\tikzset{mystyle}\xdef\outerxsep{\pgfkeysvalueof{/pgf/outer xsep}}\egroup%
}

\Xouterxsep因此调用宏后\outerxsep包含我正在寻找的值。

但是,我更喜欢更优雅的解决方案,即\pgfkeysvalueof直接使用类似的东西。我已经尝试了一些方法:

\def\pgfkeysvaluefromstyle#1#2{\begingroup\tikzset{#1}\pgfkeysvalueof{/pgf/#2}\endgroup}

这在简单情况下确实有效,但是在我的特定情况下却不行,因为我将值\titlespacing*titlesec包中输入到命令中。我猜这是某种扩展问题。我试图在titlesec源代码中查找那里实际发生了什么,但没有找到太多;titlesec代码有点不透明。

完全不工作的 MWE:

\documentclass{article}
\usepackage{tikz}
\usepackage[explicit]{titlesec}

\tikzset{mystyle/.style={outer xsep=10pt, outer ysep=10pt}}

% this does not work: pdflatex hangs, interrupting it with Ctrl+C yields
% ! Interruption.
%  \pgfkeys@parse ...uturelet \pgfkeys@possiblerelax 
%                                                   \pgfkeys@parse@main 
%  l.16   \section{Section}
%
\def\pgfkeysvaluefromstyle#1#2{\begingroup\tikzset{#1}\pgfkeysvalueof{/pgf/#2}\endgroup}
\titlespacing*{\section}{\pgfkeysvaluefromstyle{mystyle}{outer ysep}}{0pt}{0pt}

% this does work:
%
% \def\Xouterxsep{\bgroup\tikzset{mystyle}\xdef\outerxsep{\pgfkeysvalueof{/pgf/outer xsep}}\egroup}
% \Xouterxsep
% \titlespacing*{\section}{\outerxsep}{0pt}{0pt}


\begin{document}
  \section{Section}
\end{document}

答案1

事实证明,这是对处理程序(未知和其他)和系统使用密钥目录的良好宣传。我的策略是,为了找出样式的作用/mystyle/.style = {inner xsep = 2pt, outer xsep = 10pt},您必须通过更改目录来“拦截”其执行,.unknown处理程序可以使其尝试设置的密钥保存其参数。

/my style这实际上比简单地运行并尝试查找inner xsep和的值要强大得多outer xsep,因为这些键可能(几乎肯定不会)实际上只是存储值,而是进行某种奇特的内部处理。(参见安德鲁·斯泰西的评论。)

用法:

  • 写入\pgfkeys{/mystyle/.peek}以查看内部,然后查看/peek/mystyle/inner xsep等等以获取它们将被分配的值。如果/mystyle接受一个参数(只有一个),您可以像传递它一样传递它/mystyle/.peek = <arg>,结果将保存并进行正确的替换。但是,如果您真的想要<arg>括号,您必须双括号,因为它会被读取两次。不幸的是,我对此无能为力。

  • 中的键不能包含斜杠。如果包含,则无法在(嗯, )目录/mystyle中正确处理它们,并且这将不起作用。抱歉,没有办法解决这个问题;这就是处理程序的工作方式。本身可能包含或不包含斜杠。/peek/@peek/mystyle

  • 不再需要.peek用组来包围呼叫,因为/peek/mystyle每次呼叫时目录都会自动重置风格/mystyle。因此,可以维护多个并行的预览目录(cjorssen 的想法),每个目录在每次后续预览相同样式时都会完全更新,但其他方面不会受到干扰。

  • 我还提供了\pgfkeysvaluefromstyle{<full style key>}{style arg = <value>, key = <key>},它会打印其结果(Daniel 的想法),可能还会应用一个参数(必须是三倍-braced,因为它被读取了三次)。注意:不可扩展,因为\pgfkeys不可扩展。当然,这\pgfkeys{/mystyle/.peek}也适用于,但重要的是,随后调用\pgfkeysvalueof{/peek/mystyle/inner ysep} 可擴展。

这是我的代码,包括对 MWE 的应用,以及一堆其他无聊的测试,以证明它按预期工作。之后是一段很长的解释。

\documentclass{article}
\usepackage{filecontents}

\begin{filecontents}{pgfkeys-peek.sty}
\RequirePackage{pgfkeys,pgffor}

\pgfkeys{
 /@peek/.is family, /@peek,
 reset/.code = {%
  \edef\peekdir{peek\pgfkeysvalueof{/@peek/style}}%
  \pgfkeysifdefined{/@\peekdir/contents}{}{\pgfkeyssetvalue{/@\peekdir/contents}{}}%
  \pgfkeysgetvalue{/@\peekdir/contents}{\List}%
  \foreach \key in \List {%
   % \global should work, since \pgfkeyslet expands to \let(etc.)
   % \undefined is undefined!
   \global\pgfkeyslet{/\peekdir/\key}{\undefined}
  }%
  \pgfkeyssetvalue{/@\peekdir/contents}{}%
 },
 .unknown/.code = {%
  \edef\peekdir{peek\pgfkeysvalueof{/@peek/style}}%
  \edef\next{\noexpand\pgfkeys{
   /\peekdir/\pgfkeyscurrentname/.initial = {\unexpanded{#1}},
   /@\peekdir/contents/.append = {\pgfkeyscurrentname,},
  }}\next
 },
 style/.initial = {},
 % Need double braces around #1 if you want it braced here
 /handlers/.peek/.code = {%
  \edef\next{\noexpand\pgfkeys{
   /@peek,
   style=\pgfkeyscurrentpath,
   reset,
   \pgfkeyscurrentpath = #1
  }}\next
 },
}

\pgfkeys{
 /value from style/.is family, /value from style,
 style arg/.initial = {\pgfkeysnovalue},
 key/.initial = {},
}
% #1 = full key name of a style
% #2 = the above keys, with key = *relative* key name mandatory
\newcommand\pgfkeysvaluefromstyle[2]{%
 % The group is for the /value from style keys
 \begingroup
  \pgfkeys{/value from style,#2}%
  \pgfkeys{#1/.peek/.expand twice = \pgfkeysvalueof{/value from style/style arg}}%
  \pgfkeysvalueof{/peek#1/\pgfkeysvalueof{/value from style/key}}%
 \endgroup
}
\end{filecontents}

% MWE example

\usepackage{tikz}
\usepackage[explicit]{titlesec}
\usepackage{pgfkeys-peek}

\tikzset{mystyle/.style={outer xsep=10pt, outer ysep=10pt}}

\tikzset{mystyle/.peek}
\titlespacing*{\section}{\pgfkeysvalueof{/peek/tikz/mystyle/outer ysep}}{0pt}{0pt}

\begin{document}
 \section{Code examples}% Following line should have no indent
 \pgfkeys{/a style/.style = {key 1 = x, key 2 = 6}}
 \pgfkeys{/a style/.peek}
 key 1: \pgfkeysvalueof{/peek/a style/key 1}\par
 key 2: \pgfkeysvalueof{/peek/a style/key 2}\par
 key 3: \pgfkeysvalueof{/peek/a style/key 3}\par

 \medskip
 \pgfkeys{/a style/.style = {key 3 = $\pi$}}
 \pgfkeys{/a style/.peek}
 key 1: \pgfkeysvalueof{/peek/a style/key 1}\par
 key 2: \pgfkeysvalueof{/peek/a style/key 2}\par
 key 3: \pgfkeysvalueof{/peek/a style/key 3}\par

 \medskip
 \pgfkeys{/another style/.style = {key 2 = (#1)}}
 \pgfkeys{/another style/.peek = 2}
 key 1: \pgfkeysvalueof{/peek/another style/key 1}\par
 key 2: \pgfkeysvalueof{/peek/another style/key 2}\par
 key 3: \pgfkeysvalueof{/peek/another style/key 3}\par
 (/a style)\par
 key 1: \pgfkeysvalueof{/peek/a style/key 1}\par
 key 2: \pgfkeysvalueof{/peek/a style/key 2}\par
 key 3: \pgfkeysvalueof{/peek/a style/key 3}\par

 \medskip
 \pgfkeysvaluefromstyle{/another style}{style arg = 7, key = key 2}\par
 \pgfkeysvaluefromstyle{/another style}{key = key 2}\par
\end{document}

这曾经是更直接的,但现在不那么直接了。下面的 TeX“伪代码”可能更容易理解:

% /STYLE = \pgfkeysvalueof{\@peek/style}
\pgfkeys{/@peek/.is family, /@peek,
 reset/.code = {
  \foreach \key in \pgfkeysvalueof{/@peek/STYLE/contents} {
   \pgfkeyslet{/peek/STYLE/\key}{\undefined}
  }
  \pgfkeyssetvalue{/@peek/STYLE/contents}{}
 },
 .unknown/.style = {
  /peek/STYLE/\pgfkeyscurrentname/.initial = {#1},
  /@peek/STYLE/contents/.append = {\pgfkeyscurrentname}
 },
 style/.initial = {},
 /handlers/.peek/.style = {
  /@peek, style = \pgfkeyscurrentpath, reset, \pgfkeyscurrentpath = #1
 }
}

如果伪代码确实起作用(但由于扩展问题和\pgfkeyscurrentname被重新定义,它并没有起作用,更不用说/STYLE),它将按如下方式工作:

  • 所有的钥匙都/peek/STYLE落入/STYLE完整路径你偷看的风格。

  • 有一个未知的处理程序,当处理程序本身/@peek/.unknown“运行”这些键时,它会设置这些键。/@peek.peek

  • 的机制.peek存储在稍微平行的目录中/@peek。这样做的原因是,我发现如果您检查\pgfkeysvalueof{/peek/STYLE/some other key},其中some other key没有被设置/STYLE,那么随后的查看/STYLE将无法正确处理,some other key因为它将被定义为(通过\csname...\endcsname,在 中内部调用\pgfkeysvalueof)为\relax,而不是实际上保持未定义状态。因此,我需要将.unknown处理程序与放置密钥的位置分开。

  • 有一个键/@peek/reset实际上会重新取消定义之前设置的所有键/peek/STYLE(通过\let将它们设置为未定义的控制序列)。它通过查阅列表 来实现此目的,该列表由处理程序在处理 的每个键设置时/@peek/STYLE/contents维护。.unknown/STYLE

  • 处理程序.peek本身会将 dir 更改为/@peek,设置\STYLE,重置 中的键/peek/STYLE,然后/STYLE自行运行(使用您传递的任何参数)。

以下是伪代码与真实代码的不同之处以及原因:

  • 当然,在 中/@peek/reset我不能使用/STYLE,因此为了提高视觉效率,我将路径的大部分公共部分存储在宏 中\peekdir。我还必须检查 是否/peek/STYLE/contents已定义,否则,\List应该为的宏\let反而会被设置为,这就是将其应用于未知控制序列时\relax得到的。这会搞砸,因为不同于空列表。最后, 必须是 ,因为循环在本地执行其主体(令人惊讶的是,这由于 的扩展而有效)。\csname...\endcsname\foreach\relax\pgfkeyslet\global\foreach\pgfkeyslet

  • /@peek/.unknown:与 相同/STYLE。此外,我需要完全扩展要设置的键列表,因为它们\pgfkeyscurrentname在开头包含 not。每次调用处理程序时,\pgfkeyscurrentname都会重新定义,这意味着它只能在 中要调用的第一个键的名称中有效地使用.style

  • /handlers/.peek:与 的情况相同\pgfkeyscurrentpath( 存在与 相同的问题\pgfkeyscurrentname)。

答案2

编辑2012 年 2 月 1 日

一个想法是让事物变得可扩展。这是第一次尝试。

\tikzset{my style/.style = {minimum width = 2cm,inner xsep=3cm}}

\def\pgfkeysmarkkey#1{%
  \expandafter\def\csname pgfkeysvaluefromstyle@ii@#1\endcsname
    ##1,##2#1##3=##4,##5\pgfeov{##4}}

\def\pgfkeysvaluefromstyle#1{%
  \expandafter\expandafter\expandafter\pgfkeysvaluefromstyle@i
    \csname pgfk@/tikz/#1/.@cmd\endcsname\pgfeov}

\def\pgfkeysvaluefromstyle@i\pgfkeysalso#1#2{%  
  \csname pgfkeysvaluefromstyle@ii@#2\endcsname,#1,\pgfeov}

\pgfkeysmarkkey{minimum width}
\pgfkeysvaluefromstyle{my style}{minimum width}

一些注释。定义样式时,.style处理程序会定义一个宏\pgfk@<long path to the key>/.@cmd,其中<long path to the key>/tikz/my style针对 定义的样式\tikzset{my style/.style = ...}

此宏接受一个由 分隔的参数\pgfeov。在此宏中,样式的整个定义存储为 的参数\pgfkeysalso。因此,如果使用 定义my style\tikzset{my style/.style = {minimum width = 2cm, inner xsep = 3cm}}则宏\pgfk@/tikz/my style/.@cmd(使用 调用,\pgfk@/tikz/my style/.@cmd<arg>\pgfeov其中<arg>可能为空参数)将扩展为\pgfkeysalso{minimum width = 2cm, inner xsep = 3cm}

这个想法是摆脱\pgfkeysalso获取key = val样式列表并解析列表key = val以获取我们正在寻找的值。

为了使其可扩展,我认为除了“标记”我们正在寻找的密钥之外没有其他解决方案。

这是工作示例。

\documentclass{article}
\usepackage{tikz}
\usepackage[explicit]{titlesec}

\makeatletter

\def\pgfkeysmarkkey#1{%
  \expandafter\def\csname pgfkeysvaluefromstyle@ii@#1\endcsname
    ##1,##2#1##3=##4,##5\pgfeov{##4}}

\def\pgfkeysvaluefromstyle#1{%
  \expandafter\expandafter\expandafter\pgfkeysvaluefromstyle@i
  \csname pgfk@/tikz/#1/.@cmd\endcsname\pgfeov}

\def\pgfkeysvaluefromstyle@i\pgfkeysalso#1#2{%  
  \csname pgfkeysvaluefromstyle@ii@#2\endcsname,#1,\pgfeov}

\makeatother

\tikzset{mystyle/.style={outer xsep=10pt, outer ysep=10pt}}

\pgfkeysmarkkey{outer ysep}

\titlespacing*{\section}{\pgfkeysvaluefromstyle{mystyle}{outer ysep}}{0pt}{0pt}

\begin{document}
  \section{Section}
\end{document}

这是我的建议(我选择使用minimal width而不是,outer xsep但我希望很明显这是相同的)。这可能不是最好的方法,但目前,我没有其他想法。

这个想法是使用密钥处理程序.forward to,以便任何调用mystyle都会触发密钥/tikz/save my style minimum width,从而执行两件事:

  • 将键(本地至组)设置为用样式定义/tikz/my style minimum width的值。minimum widthmystyle
  • 设置(全局)宏\mystyleminimumwidthvalue,使其扩展为minimum width用样式定义的值mystyle

当样式在命令中使用一次时\draw,选项将在本地设置,因此不是可以访问命令之外保存的值\draw(请参见下面示例中的\node第一个中的最后一个)。(请注意,需要使用技巧,因为出于某种原因,由键调用的不喜欢作为参数[我可能错了])。tikzpicture\edef\x\pgfmathparseminimum width\pgfkeysvalueof

在此处输入图片描述

\documentclass{standalone}
\usepackage{tikz}

\tikzset{%
  mystyle/.style = {%
    minimum width = 2cm},
  mystyle/.forward to = /tikz/save my style minimum width,
  save my style minimum width/.code = {%
    \pgfkeyssetvalue{/tikz/my style minimum width}{%
      \pgfkeysvalueof{/pgf/minimum width}}%
    \xdef\mystyleminimumwidthvalue{%
      \pgfkeysvalueof{/pgf/minimum width}}},
  my style minimum width/.initial = \pgfkeysvalueof{/pgf/minimum width}}

\begin{document}
\begin{tikzpicture}
  \node[draw,mystyle] at (0,0) {\pgfkeysvalueof{/pgf/minimum width}};
  \node[draw,red,minimum width = 5cm] at (2,0)
    {\pgfkeysvalueof{/pgf/minimum width}}; 
  \begingroup
  \edef\x{%
    \endgroup
    \noexpand\node[draw,blue,minimum width = \pgfkeysvalueof{/tikz/my style
      minimum width}] at (4,0) {\pgfkeysvalueof{/tikz/my style
      minimum width}};}
  \x
\end{tikzpicture}
\begin{tikzpicture}[mystyle]
  \begingroup
  \edef\x{%
    \endgroup
    \noexpand\node[draw,blue,minimum width = \pgfkeysvalueof{/tikz/my style
      minimum width}] at (4,0) {\pgfkeysvalueof{/tikz/my style
      minimum width}};}
  \x
\end{tikzpicture}
\end{document}

相关内容