10

I'm using pgfkeys, and a fairly adventurous syntax in which the values for some keys contain additional key/value pairs. (For instance, the value of the nodes key is a list of pairs, and the second component of each pair is a key-value list.)

I get a compilation error whenever I put anything too fancy into the label key. The code below works fine as-is, but if I replace 2+2 with 2+\sqrt{2}, it breaks. I think it's a macro-expansion problem. How can I arrange it that the contents of the label key does not get expanded until it needs to be?

Code

\documentclass{article}
\usepackage{tikz}

\makeatletter

\pgfkeys{/wickerson/.cd,
  % The following two lines are from:
  % http://tex.stackexchange.com/q/85637/86
  execute style/.style = {#1},
  execute macro/.style = {execute style/.expand once=#1},
  left/.code=            {\xdef\wickerson@left{#1}},
  top/.code=             {\xdef\wickerson@top{#1}},
  label/.code=           {\gdef\wickerson@label{#1}},
  nodes/.code=           {\xdef\wickerson@nodes{#1}},
  colour/.code=          {\xdef\wickerson@colour{#1}},
}

\newcommand\myDiagram[1][]{%
  \pgfkeys{/wickerson/.cd,colour=black,nodes={},#1}
  \node[text=\wickerson@colour] at (0,0) {My Diagram...};
  \foreach \i/\values in \wickerson@nodes {
    \pgfkeys{/wickerson/.cd,left=0,top=0,label={},%
      execute macro=\values}
    \node[shape=circle,draw=black]
      at (\wickerson@left, \wickerson@top) 
      {\wickerson@label};
  }
}

\makeatother

\begin{document}

\begin{tikzpicture}[x=1mm,y=-1mm]
\myDiagram[% 
  colour=red, %
  nodes={%
    a/{left=0,top=10,label={$2+2$}}, %
    b/{left=15,top=10,label={$\log 4$}}%
  }%
]
\end{tikzpicture}

\end{document}

Output

enter image description here

  • 1
    You can add \noexpand before the \sqrt, but I guess that's not the solution you're looking for. – Jake May 27 '13 at 12:48
  • Thanks Jake. Yeah, I guess I'm hoping for something that will apply \noexpand to everything in the label, so to speak. – John Wickerson May 27 '13 at 13:36
  • 1
    In the MWE you neither need global assignments nor expanded ones. In each of the keys a simple \def should suffice. Then there also won't be an error with $2+\srqt{2}$ – cgnieder May 27 '13 at 15:47
  • 3
    @cgnieder This is probably an answer. Also, with eTeX, \edef\foo{\unexpanded{#1}} is slightly better than \def\foo{#1}, because it will correctly escape macro parameter characters # that are given inside the key, for even wilder labels. – Bruno Le Floch May 27 '13 at 15:51
  • 1
    The specific problem is that nodes is expanded, not label. – Ryan Reich May 27 '13 at 16:20
  • @RyanReich Aha! I haven't tried it yet, but I think you've hit the nail on the head. Thanks. – John Wickerson May 27 '13 at 16:33
  • The adventure is not the additional key=value syntax but using existing names for your keys. Even if you fix this, 67 other issues would pop up if somehow you forget to change directory to wickerson family or changing directory is delayed. – percusse May 27 '13 at 17:13
  • 1
    @percusse Oh. Hm. I thought the whole point of the directory structure in pgfkeys was so that you could use existing names like label for your keys! – John Wickerson May 27 '13 at 17:19
  • You could indeed. That's the adventure :) – percusse May 27 '13 at 18:31
  • @percusse I would not consider that a problem. In fact, it's sort of a feature, if you want to "intercept" existing keys to add functionality to them in a transparent manner. – Ryan Reich May 27 '13 at 18:36
  • @RyanReich It is indeed but pgfplots has that feature too and sometimes it is really difficult to differentiate the TikZ family from the pgfplots family especially with interoperation say drawing features and plot features. – percusse May 27 '13 at 18:39
  • @JohnWickerson If you are going to use it extensively you might switch to /.store in and /.estore in keys to avoid those problems. – percusse May 27 '13 at 20:11
  • @percusse How would that avoid those problems? – John Wickerson May 27 '13 at 20:13
  • You won't be defining as you do with the /.code, it's a little less tedious work and easier to maintain. – percusse May 27 '13 at 20:14
  • @percusse Yes, I see. And the estore/store syntax makes it a little clearer what one is trying to achieve. Thanks for the suggestion. By the way, I realise that I don't understand what you meant by your earlier comment about changing directory being delayed - could you say a little more about that? – John Wickerson May 28 '13 at 04:14
  • I've tried to make it a little clearer in your newer question. If the directory cannot be changed then all your custom styles will obtain their original meaning and things would get difficult to debug. – percusse May 28 '13 at 20:56

2 Answers2

4

Just to confirm that cgnieder's comment and Ryan Reich's explanation were right on target. I have managed to fix my code and understand the problem too. Thanks folks!

\documentclass{article}
\usepackage{tikz}

\makeatletter

\pgfkeys{/wickerson/.cd,
  % The following two lines are from:
  % https://tex.stackexchange.com/q/85637/86
  execute style/.style = {#1},
  execute macro/.style = {execute style/.expand once=#1},
  left/.code=            {\def\wickerson@left{#1}},
  top/.code=             {\def\wickerson@top{#1}},
  label/.code=           {\def\wickerson@label{#1}},
  nodes/.code=           {\def\wickerson@nodes{#1}},
  colour/.code=          {\def\wickerson@colour{#1}},
}

\newcommand\myDiagram[1][]{%
  \pgfkeys{/wickerson/.cd,colour=black,nodes={},#1}
  \node[text=\wickerson@colour] at (0,0) {My Diagram...};
  \foreach \i/\values in \wickerson@nodes {
    \pgfkeys{/wickerson/.cd,left=0,top=0,label={},%
      execute macro=\values}
    \node[shape=circle,draw=black]
      at (\wickerson@left, \wickerson@top) 
      {\wickerson@label};
  }
}

\makeatother

\begin{document}

\begin{tikzpicture}[x=1mm,y=-1mm]
\myDiagram[% 
  colour=red, %
  nodes={%
    a/{left=0,top=10,label={$\sqrt{2}+2$}}, %
    b/{left=15,top=10,label={$\log 4$}}%
  }%
]
\end{tikzpicture}

\end{document}
  • What are \i, a, b doing in your scheme? Also, the key execute style seems redundant. – Ahmed Musa May 27 '13 at 23:22
  • @Ahmed if I may answer for John: I expect that a and b (as stored in \i) are intended to be used as node names eventually. As for execute style, it is necessary to have a key to "hang" the .expand once handler on, since the result of a handler is always passed up to the parent key. – Ryan Reich May 28 '13 at 04:07
  • @RyanReich: What of execute macro/.expand once=#1? – Ahmed Musa May 29 '13 at 17:43
3

Here is a solution with skeyval package.

\documentclass{article}
\usepackage{tikz}
\usepackage{skeyval}[2013/05/01]
\makeatletter
\directkeys{%
  .family=wickerson,
  .holder prefix=wic@,
  .define keys={%
    .cmd/left/0,
    .cmd/top/0,
    .cmd/label//,
    .cmd/colour/black,
    .zcmd/title colour/black,
    .ord/nodes//
      \skvifblankTF{#1}{}{%
        \node[text=\wic@titlecolour] at (0,0) {My Diagram ...};
        \edef\tempa{\unexpanded{#1}}%
        \skvstripouterbraces{1}\tempa
        \newforeach [expand list once] \x in \tempa {%
          % Use the parameter rep of \x:
          \skvsetkeys{wickerson}{##1}%
          \node[shape=circle,draw=\wic@colour] at (\wic@left,\wic@top){\wic@label};
        }%
      },
  },
  % Allow 'color' to be a clone of 'colour':
  .link=color/colour,
}
\newcommand\myDiagram[1][]{%
  \directkeys{%
    .family=wickerson,
    .set keys={nodes,left,top,label,colour,title colour},
    .set keys={#1}
  }%
}
\makeatother
\begin{document}
\begin{tikzpicture}[x=1mm,y=-1mm]
\myDiagram[%
  title colour=green,
  nodes={
    {color=red,left=0,top=10,label={$\sqrt{2}+2$}},
    {color=blue,left=15,top=10,label={$\log 4$}}
  }%
]
\end{tikzpicture}
\end{document}

Note: I don't have the jpeg, png, tiff, gif or bmp format of output that this site is insisting on.

Ahmed Musa
  • 11,742