使用 pgfkeys,我想要一个选择键,它在选择一个值时不是立即执行一些代码,而是将选择存储在某个地方,然后稍后根据需要执行与该选项相关的代码。
举个例子应该能说明问题。我想定义某种处理程序,这样我就可以编写类似以下内容的代码:
\pgfkeys{
/action/.is choice,
/action/jump/.on exec = \dojump,
/action/walk/.on exec = \dowalk,
/action/run/.on exec = \dorun
}
这样当我选择一个选项时
\pgfkeys{/action=jump} # remembers that I selected "jump"
相应的代码\dojump
是不是执行,但选择被保存并稍后执行,直到我实际说出类似以下内容:
\pgfkeys{/action/.exec} # actually does the `\dojump`
这应该允许用户在某个时候选择一个选项,然后稍后改变主意,然后将该选项放回去。但直到我明确要求执行所选选项的代码时,才应该执行任何操作。
有没有一种简单的方法可以向 pgfkeys 添加处理程序并实现此目的?我做事的方式正确吗,还是我在重新发明轮子?
答案1
在对我的原始想法进行了几次迭代之后,我实现了一个我现在非常满意的解决方案,并且它足够稳定,所以我很乐意与大家分享它。
我把它变成了一个小包裹埃兹凯斯,这是一个单独的文件,主要包含文档和一些定义新 pgfkey 处理程序的代码。
作为示例用法,设置后:
\pgfkeys{
my package/.is family,
my package,
align/.is code choice,
align/left/.code = \raggedright,
align/center/.code = \centering,
align/right/.code = \raggedleft,
format/.is code choice,
format/bold/.code = \textbf{#1},
format/italics/.code = \textit{#1},
}
可以使用以下方式选择选项:
\pgfkeys{my package,align=right,format=bold}
这会导致记住所选的选项,但是不是执行。只有在稍后通过明确评估来请求时
\pgfkeys{my package,align/.eval,/format/.eval=Hello}
然后相应的子键将被执行和扩展,在这个例子中是
\raggedleft\textbf{Hello}
答案2
解决方案 (2012/11/02)
我认为我们不需要鍵盤对于这个任务,但这是一个尝试。
\documentclass{article}
\usepackage{pgfkeys}
\makeatletter
\def\elk@do#1{\@ifnextchar x#1#1}
\def\elk@@do#1#2#3{%
\csname @\ifx#1#2gobble\else iden\fi\endcsname
{#3\elk@do#1}%
}
\def\foreachelk#1#2{%
\def\elkdo##1,{\elk@@do\elkdo{##1}{#2}}%
\elk@do\elkdo#1,\elkdo,%
}
\def\foreachbison#1#2{%
\def\bisondo##1/##2,{\elk@@do\bisondo{##1}{#2}}%
\elk@do\bisondo#1,\bisondo/,%
}
\def\pgfkeys@everystartchoice{}
\def\pgfkeyseverysoc{\g@addto@macro\pgfkeys@everystartchoice}
\def\pgfkeys@everyendchoice{}
\def\pgfkeyseveryeoc{\g@addto@macro\pgfkeys@everyendchoice}
\let\nava@choicewrapper\@iden
\def\choicewrapper{\gdef\nava@choicewrapper}
\pgfkeys{
/nava/action/.is family,
/nava/action/.cd,
.define choices/.code={%
\foreachbison{#1}{%
\pgfkeysalso{%
/nava/action/##1/.code={%
\ifcsname nava-##1\endcsname
\@latexerr{Choice '##1' already defined}\@ehd
\else
\expandafter\edef\csname nava-##1\endcsname{%
\unexpanded\expandafter{\pgfkeys@everystartchoice}%
\unexpanded{\nava@choicewrapper{##2}}%
\unexpanded\expandafter{\pgfkeys@everyendchoice}%
}%
\fi
},
/nava/action/##1,
}%
}%
},
.exec method/.code={%
\foreachelk{#1}{\csname nava-##1\endcsname}%
},
}
% \addnewchoices{<choice1/method1,choice2/method2,...>}
\def\addnewchoices#1{\pgfkeys{/nava/action/.define choices={#1}}}
\def\dochoicemethods#1{\pgfkeys{/nava/action/.exec method={#1}}}
\makeatother
\begin{document}
% These are optional. Insert at end of every choice:
\pgfkeyseveryeoc{.\endgraf}
% Wrap the methods for the choices:
\choicewrapper{\textit}
% Define and remember my choices:
\addnewchoices{jump/I'll jump,walk/I'll walk,run/I'm running home}
% Call my choices and execute their methods:
\dochoicemethods{jump,walk,run,run,walk}
\dochoicemethods{jump}
\end{document}
答案3
.style
通过将所需的代码分配给辅助键,可以轻松实现这种延迟评估.exec
。
\documentclass{article}
\usepackage{pgfkeys}
\begin{document}
\newcommand\dojump{jump}
\newcommand\dowalk{walk}
\newcommand\dorun{run}
\pgfkeys{
/action/.is choice,
/action/jump/.style={/action/.exec/.code={\dojump}},
/action/walk/.style={/action/.exec/.code={\dowalk}},
/action/run/.style={/action/.exec/.code={\dorun}}
}
\pgfkeys{/action=jump} % remembers that I selected "jump"
\pgfkeys{/action/.exec} % actually does the `\dojump`
\end{document}
答案4
这个问题问得非常好。在我目前的项目中,我经常遇到需要这种行为的情况。不过,我认为可能存在比你提出的更简单的解决方案。
下面是它的工作原理:
- 首先,我们定义一个新的处理程序,可以像 一样使用
/some/key/.is deferred choice
。这将执行/some/key/.is choice
并定义一个'镜像键'/deferred choice/code/some/key/.is choice
。此镜像键将具有相同的选择,它将存储实际的代码。 - 然后用 定义各个选项
/some/key/<choice>/.deferred code={<code>}
。这会将 存储<code>
在 中/deferred choice/code/some/key/<choice>/.code
,并设置/some/key/<choice>/.code
为仅存储值以供以后使用。 - 设置时,使用
/some/key=<choice>
将值<choice>
存储在键中。/deferred choice/value/some/key
\pgfkeyssetvalue
- 在运行时,会加载来自的
/some/key/.execute=#1
值,以便随后找到并执行键。<choice>
/deferred choice/value/some/key
/deferred choice/code/some/key/<choice>={#1}
下面是示例的编译输出。如您所见,设置align=right, format=bold
没有输出任何内容,因为实际代码的执行被推迟到.execute
运行时。
\documentclass{article}
\usepackage{pgfkeys}
\makeatletter
\pgfkeysdef{/handlers/.deferred code}{% is called with \pgfkeyscurrentpath=/path/ChoiceKey/ChoiceValue
\pgfkeysdef{/deferred choice/code\pgfkeyscurrentpath}{#1}%
\edef\pgfkeyscurrentkey{\pgfkeyscurrentpath}\pgfkeys@split@path% now path=/path/ChoiceKey and name=ChoiceValue
\pgfkeysedef{\pgfkeyscurrentkey}{\noexpand\pgfkeyssetvalue{/deferred choice/value\pgfkeyscurrentpath}{\pgfkeyscurrentname}}%
}
\makeatother
\pgfkeysdef{/handlers/.is deferred choice}{%
\expandafter\pgfkeysedef{\pgfkeyscurrentpath/.execute}{% is called with \pgfkeyscurrentpath=/path/ChoiceKey
\noexpand\pgfkeysgetvalue{/deferred choice/value\pgfkeyscurrentpath}{\noexpand\temp}%
\noexpand\pgfkeysalso{/deferred choice/code\pgfkeyscurrentpath/\noexpand\temp={##1}}%
}
\pgfkeysalso{\pgfkeyscurrentpath/.is choice}%
\pgfkeysalso{/deferred choice/code\pgfkeyscurrentpath/.is choice}%
}
\begin{document}
\pgfkeys{
/my package/.is family,
/my package,
align/.is deferred choice,
align/left/.deferred code = left:,
align/right/.deferred code = right:,
format/.is deferred choice,
format/bold/.deferred code = bold:{#1},
format/italics/.deferred code = italics:{#1},
}
\pgfkeys{/my package, align=right, format=bold}
\pgfkeys{/my package, align=left, format=italics}
%\pgfkeys{/my package, align=wrong} % Error: Choice 'wrong' unknown in choice key '/my package/align'
\pgfkeys{/my package, align/.execute, format/.execute=Hello}
\end{document}