Is there a way to set a *global* key value using `pgfkeys`?
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}
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}
First, you want to use \pgfkeyslet
instead of \pgfkeyssetvalue
, since you want to use the key outside, where \myvalue
is no longer defined, so you have to set the key to the contents of \myvalue
instead of to \myvalue
itself.
Second, the definition of \pgfkeyslet
starts with \expandafter\let
. As it happens, prefixing \pgfkeyslet
with \global
still affects the \let
after the expandafter
, so that
\foreach \mykey/\myvalue in {long/list, of/keys}
{\global\pgfkeyslet{\mykey}{\myvalue}}
does what you want. This solution is a bit dirty since it depends on the internal definition of \pgfkeyslet
.
Another solution is to forget about \foreach
if it doesn't fit the bill and to use another method for iterating about a comma separated list. If you are free to choose the representation of the list that you want to iterate over, the simplest approach is the one proposed by Knuth in Appendix D of The TeXbook. Define the list as
\newcommand\mylist{\li{long}{list}\li{of}{keys}}
Now you can process the list items repeatedly by \let
ing \li
to a command that implements the loop body. In your example this would just be
\let\li\pgfkeyssetvalue
after which
\mylist
processes the key-value pairs. (Knuth proposed to use \\
instead of \li
, which might be dangerous in LaTeX.)