9

Using pgfkeys, I would like to have a choice key which, rather than immediately executing some code when a value is selected, stores the selection somewhere and then later on demand I can execute the code associated with said option.

An example should clarify things. I would like to define some sort of handler that allows me to write something like:

\pgfkeys{
  /action/.is choice,
  /action/jump/.on exec = \dojump,
  /action/walk/.on exec = \dowalk,
  /action/run/.on exec  = \dorun
}

So that when I select an option

\pgfkeys{/action=jump} # remembers that I selected "jump"

the corresponding code \dojump is not executed just yet, but instead the selection is saved and executed only later until I actually say something like:

\pgfkeys{/action/.exec} # actually does the `\dojump`

This should allow the user to choose one option at some point, and change her mind later, and then put the option back. But nothing should be executed until I explicitly ask the code for the selected option to be executed.

Is there a simple way to add handlers to pgfkeys and achieve this? Am I doing things the right way, or I am trying to reinvent the wheel?

2
  • I can't see what \pgfkeys@split@path is doing here, since you aren't using \pgfkeyscurrentname and \pgfkeys@case@three calls \pgfkeys@split@path. And do exec/.style={\pgfkeyscurrentpath/do exec} smacks of a cyclic call.
    – Ahmed Musa
    Commented Nov 2, 2012 at 18:28
  • \pgfkeys@split@path splits the current key into path+name; by setting the path to the current key before calling this, effectively this moves the path “one level up”. And the “cyclic call” is no cyclic, precisely for the same reason, I do a .cd up to go one leve up before. Commented Nov 2, 2012 at 23:53

4 Answers 4

6

After a few more iterations of my original ideas, I've implemented a solution with which I'm pretty comfortable now, and which is stable enough so that I'm happy to share it as well.

I've turned this into a small package ezkeys, which is a single file containing mostly documentation and a bit of code defining some new pgfkey handlers.

As an example usage, after setting:

\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},
}

Options can be selected using, e.g.:

\pgfkeys{my package,align=right,format=bold}

which causes the selected options to remebered but not executed. Only later, when requesting it by explcitly evaluating

\pgfkeys{my package,align/.eval,/format/.eval=Hello}

then corresponding subkeys will be executed and expanded, in this example to

\raggedleft\textbf{Hello}
3

Solution (2012/11/02)

I don't think we need pgfkeys for this task, but here is an attempt.

\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}
5
  • Sure, that is what I want to achieve. However (1) I don't want to have to invent a new name (\navaaction) for each similar option that I may want to define. It feels that something like /navarro/action/exec is already a good name that shouldn't clash with other packages. And (2) I'm lazy, if I do this over and over again, I don't want to be writing \def\navaaction all the time. That's why I think it would be better to define a handler that does all the work for me. Commented Nov 1, 2012 at 20:41
  • You can get rid of the macro by writing, say, action/jump/.style = {exec/.code = \dojump}.
    – Ryan Reich
    Commented Nov 1, 2012 at 23:09
  • And what should I use to call the last action selected by the user? Commented Nov 2, 2012 at 14:44
  • @JuanA.Navarro: The last question isn't clear to me.
    – Ahmed Musa
    Commented Nov 2, 2012 at 15:08
  • @AhmedMusa, sorry for that. I guess I could have explained myself better. Anyway, thanks for your help, now I've updated my question with something that looks like a solution to me. Would you like to comment on that? Commented Nov 2, 2012 at 16:33
2

This type of deferred evaluation can be easily achieved by making each choice a .style which assigns the desired code to the helper key .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}
1

This is an excellent question. In my current project I encounter situations that would require this behavior very often. However, I think a simpler solution is possible than the one you propose.

Here is how it works:

  • First, we define a new handler which can be used like /some/key/.is deferred choice. This executes /some/key/.is choice and also defines a 'mirror key' /deferred choice/code/some/key/.is choice. This mirror key will have the same choices, which will store the actual code.
  • The individual options are then defined with /some/key/<choice>/.deferred code={<code>}. This stores the <code> in /deferred choice/code/some/key/<choice>/.code and sets up /some/key/<choice>/.code to only store the value for later use.
  • When setting /some/key=<choice>, the value <choice> is stored in the key /deferred choice/value/some/key using \pgfkeyssetvalue.
  • When running /some/key/.execute=#1, the value <choice> from /deferred choice/value/some/key is loaded, in order to then find and execute the key /deferred choice/code/some/key/<choice>={#1}.

And here is the compiled output of the example. As you can see, setting align=right, format=bold did not output anything, since the execution of the actual code was deferred until .execute was run.

Compiled output of the example

\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}

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .