Take the 2-minute tour ×
TeX - LaTeX Stack Exchange is a question and answer site for users of TeX, LaTeX, ConTeXt, and related typesetting systems. It's 100% free, no registration required.

The simple answer to this question appears to be "No"; quoting from the pgf manual:

The setting of a key is always local to the current TeX group.

However, this is very annoying because it means that you can't do something like:

\foreach \mykey/\myvalue in {
  long/list,
  of/keys%
} {
  \pgfkeyssetvalue{\mykey}{\myvalue}
}

So my next best shot is:

\makeatletter
\pgfkeys{/applicant/.code 2 args={\expandafter\global\expandafter\def\csname applicant@#1\expandafter\endcsname\expandafter{#2}}}
\foreach \mykey/\myvalue in {
  long/list,
  of/keys%
} {
  \pgfkeys{/applicant={\mykey}{\myvalue}}
}
\makeatletter

but that's not very transparent since to get at the "value", I have to use the macro \csname applicant@whatever the key name was\endcsname. (Also, I'm not very au fait with pgfkeys, this being my first foray, so I should probably have used something like /.expand once or /.store in, but that's by the by.)

Is there a better way to circumvent this restriction? I'd be happy with a new macro for defining the key, so \pgfglobalkeys would be okay (though in the spirit of \pgfkeys it feels more like it ought to be \pgfkeys{key name/.global=...} but let's not quibble), but when using the key's value then it ought be be accessible via the traditional means, say pgfkeysgetvalue.

share|improve this question
    
I think that all of the key-value solutions take the view that keys are local objects. –  Joseph Wright Apr 7 '11 at 8:18
    
@Joseph: I know that, but it can be annoying (since, as I said, \foreach loops are their own little scope) so I'd like to easily circumvent that! –  Loop Space Apr 7 '11 at 8:24
    
I guess my point is that this is a problem with \foreach, not with the keys side of things! –  Joseph Wright Apr 7 '11 at 8:26
    
@Joseph: So maybe I should have a \globalforeach which saves up the contents of the \foreach and then puts them in an \aftergroup. Hmm, interesting idea. –  Loop Space Apr 7 '11 at 8:33
    
I tried this once by patching the pgfkeys source code inserting \global everywhere I could find an assignment. Didn't work...so I hope you find a solution! –  Matthew Leingang Apr 7 '11 at 9:56

2 Answers 2

up vote 10 down vote accepted

Edit: I just got my copy of TeX by Topic from Lulu, which of course means that my work day ended early :-). I stumpled upon \globaldefs, which allows one to answer the question actually asked with a yes:

\documentclass{article}
\usepackage{tikz}
\begin{document}
\pgfkeys{/tmp/.cd, foo/.initial = a, bar/.initial = z}
\def\showstatus{%
  (level: \the\currentgrouplevel\ -- 
   globaldefs: \the\globaldefs\ --
   foo: \pgfkeysvalueof{/tmp/foo} --
   bar: \pgfkeysvalueof{/tmp/bar})}

\showstatus

{\globaldefs=1\relax
  \foreach \k/\v in {{/tmp/foo}/bb,{/tmp/bar}/yy} { %
    \pgfkeyssetvalue{\k}{\v}
  }
  \showstatus
}

\showstatus

\end{document}

Here I implicitly use that \globaldefs is 0 (or non-positive), so that the assignment \globaldefs=1 is local. If \globaldefs is already positive in the current scope, we don't need to set it; and in fact it would be wrong to do so (since its value might be local to some surrounding group; assigning it a positive value again would set it globally). Correcting this is left as an exercise.

Original answer Andrew, your comment about using \aftergroup led me to investigate. It turns out that the body of \foreach is actually performed two levels down. Assuming that what you really want is a way for \foreach to be able to set keys at the current scope (as opposed to actually setting them globally), this seems to work:

\documentclass{article}

\usepackage{tikz}
\usepackage{etoolbox}

\begin{document}

\pgfkeys{/tmp/.cd, foo/.initial = a, bar/.initial = z}

\def\showstatus{%
(\the\currentgrouplevel\ -- 
\pgfkeysvalueof{/tmp/foo} --
\pgfkeysvalueof{/tmp/bar})}

\makeatletter

\csdef{my@count}{0}
\newcommand*{\@csgincr}[1]{\csnumgdef{#1}{\csuse{#1} + 1}}

The initial status: \showstatus

\foreach \k/\v in {{/tmp/foo}/bb,{/tmp/bar}/yy} { %
  \@csgincr{my@count}%
%  \showstatus (\k, \v, \my@count)
  % Define a global macro which does the keyval-setting (locally), and
  % which then undefines itself
  \csxdef{@tmp@setkeyval@\my@count}{\noexpand\pgfkeyssetvalue{\k}{\v}%
    \noexpand\global\noexpand\csundef{@tmp@setkeyval@\my@count}}%
  % Define a global macro which when called, places the above
  % \aftergroup, and then undefines itself
  \csxdef{@tmp@export@\my@count}{\noexpand\aftergroup%
    \expandafter\noexpand\csname @tmp@setkeyval@\my@count\endcsname%
    \noexpand\global\noexpand\csundef{@tmp@export@\my@count}}%
  % Place the above \aftergroup
  \expandafter\aftergroup\csname @tmp@export@\my@count\endcsname%
%  \aftergroup\par
}

Now we have: \showstatus

{
Now we are on a level 1 group \showstatus
\foreach \k/\v in {{/tmp/foo}/ccc,{/tmp/bar}/xxx} { %
  \@csgincr{my@count}%
%  \showstatus (\k, \v, \my@count)
  \csxdef{@tmp@setkeyval@\my@count}{\noexpand\pgfkeyssetvalue{\k}{\v}%
    \noexpand\global\noexpand\csundef{@tmp@setkeyval@\my@count}}%
  \csxdef{@tmp@export@\my@count}{\noexpand\aftergroup%
    \expandafter\noexpand\csname @tmp@setkeyval@\my@count\endcsname%
    \noexpand\global\noexpand\csundef{@tmp@export@\my@count}}%
  %
  \expandafter\aftergroup\csname @tmp@export@\my@count\endcsname%
%  \aftergroup\par
}
The keys have been updated \showstatus
}

but only inside the group \showstatus

\makeatother

\end{document}
share|improve this answer
    
I think you should get a detective badge for this! –  Loop Space Apr 8 '11 at 6:59
    
@Andrew: Thanks, and please see the updated answer. –  Villemoes Apr 8 '11 at 19:39
    
That is fantastic. I think that both your solutions are useful, so I'm glad you left the earlier one in there. And with the added bonus that I now understand why one might want to mess with \globaldefs! (And as if that weren't enough, I'm left with the image of Lulu sending you a book. If you're confused, take a look at en.wikipedia.org/wiki/Lulu_%28singer%29 ) –  Loop Space Apr 8 '11 at 20:09
    
It is probably safer to have the \globaldefs inside the loop, so that the the looping code is not affected. Or even better make a wrapper of \pgfkeys that locally sets \globaldefs. –  Caramdir Apr 8 '11 at 20:58
    
@Caramdir: That's right. Would something as simple as \def\pgfglobalkeys#1{\begingroup \ifnum\the\globaldefs>0\relax \else \globaldefs=1\fi \pgfkeys{#1}\endgroup} work? (Provided of course that none of the key-setting business messes with \globaldefs.) –  Villemoes Apr 8 '11 at 21:52

I don't think pgfkeys allows for global assignments out-of-the-box. However, you can define your own handler to define global values. The problem is that it wont work the same for all macros like \pgfkeyssetvalue keys like .code, .store in, .style, etc. You would have to look up their definitions and define global versions of these handlers, like \pgfkeysgsetvalue .gcode, .gstore in, .gstyle.

Here the implementations of .gcode and \pgfkeysgsetvalue:

\documentclass{article}
\usepackage{pgfkeys}

\makeatletter
\pgfkeysdef{/handlers/.gcode}{%
    \long\def\pgfkeys@temp ##1\pgfeov{#1}%
    \global\pgfkeyslet{\pgfkeyscurrentpath/.@cmd}{\pgfkeys@temp}%
}
\newcommand{\pgfkeysgsetvalue}[2]{%
    \pgfkeys@temptoks{#2}%
    \expandafter\xdef\csname pgfk@#1\endcsname{\the\pgfkeys@temptoks}%
}
\makeatother
\begin{document}

\pgfkeys{test/.code={\message{NOOO}}}%
\pgfkeyssetvalue{test2}{\message{NOOO}}%
{%
\pgfkeys{test/.gcode={\message{WORKS}}}%
\pgfkeysgsetvalue{test2}{\message{WORKS AS WELL}}%
}
\pgfkeys{test}
\pgfkeysgetvalue{test2}{\test}\test

\end{document}
share|improve this answer
    
This is definitely the way to approach it. –  Matthew Leingang Jun 20 at 15:34

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.