可以说我已经定义了一些风格:
\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 width
mystyle
- 设置(全局)宏
\mystyleminimumwidthvalue
,使其扩展为minimum width
用样式定义的值mystyle
。
当样式在命令中使用一次时\draw
,选项将在本地设置,因此不是可以访问命令之外保存的值\draw
(请参见下面示例中的\node
第一个中的最后一个)。(请注意,需要使用技巧,因为出于某种原因,由键调用的不喜欢作为参数[我可能错了])。tikzpicture
\edef\x
\pgfmathparse
minimum 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}