3

I am using pgfkeys to hold formatting parameters. The tree is set up with a set of default values, as well as customizations for various formats. Those formats each have a basic definition, which in turn can have multiple subformats. I maintain all of these simultaneously because I need to be able to switch between different formats and subformats within a single document. And because I store many parameters in the tree, I don't want to have to fully define the complete list of parameters for every subformat. Instead, I take advantage of the .search also handler so that I request the parameter for a specific subformat and \pgfkeys will (assuming .search also has been setup properly) give the most specific customization that exists, falling back to the default setting if nothing else is specified.

For example, for the subformat 1 for format test one, I define the key

\pgfkeys(/stdt/.cd,
    test one/1/.search also={/stdt/test one,/stdt/default})

which has the result that if the key does not exist in that subformat, we first check the parent format, and then the default format for the key.

Then I use the following macro to retrieve the right parameter for the default context while still letting override things if I need to:

\NewDocumentCommand{\stdtParameter} { O{\ActiveFormat} O{\theTestSection} m }
    {\pgfkeys{/stdt/#1/#2/.cd,#3}}

This works fine in ordinary contexts, but it's fragile and cannot be used when I need to expand first, for example if I want to use the parameter as an argument of ifnumcomp from etoolbox.

I'm able to use \edef if I invoke \pgfkeysvalueof, but not \pgfkeys itself. For example, this works:

\edef\temp{\pgfkeysvalueof{/stdt/\ActiveFormat/num columns}}%
\ifnumgreater{\temp}{1}{Do something}{Do something else}%

But I've been unable to do the same with \pgfkeys. All my experiments with \edef and variants have led to abject failure. The problem with using \pgfkeysvalueof, though, is that it sidesteps the .search also handler mechanism, and an undefined key will return \relax, which means that I would need to reimplement all the logic of .search also in my own macro, and I'm loath to do that if it's unnecessary.

How can I properly expand \pgfkeys so that I can write something like this?

\ifnumgreater{\stdtParameter{num columns}}{1}{Do something}{Do something else}

MWE to show the effects of different kinds of calls

\documentclass{article}
\usepackage{etoolbox,xparse}
\usepackage{pgfkeys}

\newcommand{\ActiveFormat}{default}
\newcounter{TestSection}

% We need to use cd before getting the parameter because giving the full path 
% will stop \pgfkeys from checking search-also trees.
\NewDocumentCommand{\stdtParameter} { O{\ActiveFormat} O{\theTestSection} m }
    {\pgfkeys{/stdt/#1/#2/.cd,#3}}

\pgfkeys{/stdt/.cd,
    format/.store in=\ActiveFormat,
    default/0/.search also={/stdt/default},
    default/1/.search also={/stdt/default},
    default/name/.initial=Default,
    default/my size/.initial=0.5in,
    default/my label/.initial=Generic Section,
    default/num columns/.initial={1},
    test one/.search also={/stdt/default},
    test one/name/.initial=Test One,
    test one/num columns/.initial={2},
    test one/my size/.initial=0.625in,
    test one/0/.search also={/stdt/test one,/stdt/default},
    test one/1/.search also={/stdt/test one,/stdt/default},
    test one/1/my label/.initial=Some Content,
    test two/.search also={/stdt/default},
    test two/name/.initial=Test Two,
    test two/0/.search also={/stdt/test two,/stdt/default},
    test two/1/.search also={/stdt/test two,/stdt/default},
    test two/my label/.initial=A Different Section
}

\begin{document}

Original format (should be default): \ActiveFormat

Testing \textsf{\textbackslash stdtParameter}

With no optional arguments, the default \textsf{my size} should be \pgfkeys{/stdt/default/my size}. 
I got a value of \stdtParameter{my size}.

With one optional argument, \textsf{test one/my size} should be \pgfkeys{/stdt/test one/my size}. 
I got a value of \stdtParameter[test one]{my size}.

Now ask for an explicit format which does not define a value for \textsf{my size}. I should get the default value above.
I got a value of \stdtParameter[test two]{my size}

With both optional arguments (format and section), the value of \textsf{test one/1/my label} should be `\pgfkeys{/stdt/test one/1/my label}'.
I got a value of `\stdtParameter[test one][1]{my label}'.

Now check for the fallback label when it's undefined. I asked for \textsf{test two/1/my label}. I should see 
`\pgfkeys{/stdt/test two/1/.cd,my label}'. 
I got a value of \stdtParameter[test two][1]{my label}.

By default we have \stdtParameter{num columns} columns.

% Switch active formats
\pgfkeys{/stdt/format=test one}

Switched active format to \ActiveFormat, but we're still in section \theTestSection.

The label is `\stdtParameter{my label}'.

\stepcounter{TestSection}

After incrementing the section counter, the label is now `\stdtParameter{my label}' and we
have \stdtParameter{num columns} columns.
% set columns for active format/subformat
\stdtParameter{num columns=3}

Now we have \stdtParameter{num columns} columns.

% The following won't work. We need to fully expand pdfkeys and pass that to \ifnumgreater
% \ifnumgreater{\stdParameter{num columns}}{1}{Multi-column action}{One-column action}

Checking full paths: Default = \pgfkeys{/stdt/default/num columns}.

Test one = \pgfkeys{/stdt/test one/num columns}

Test one, section one = \pgfkeys{/stdt/test one/1/.cd,num columns}

Test two, section one = \pgfkeys{/stdt/test two/1/.cd,num columns}  
\end{document}
3
  • Try using /stdt/.is family instead. In the old documentation this was used to create a new "folder". Commented Dec 15, 2017 at 16:34
  • @John Kormylo Can you explain in more detail what you have in mind? I just tried the .is family methods, and the basic problem remains: I still can't get \pgfkeys to expand with \edef.
    – Karl Hagen
    Commented Dec 15, 2017 at 17:14
  • I am not an expert on using \pgfkeys, and what I learned now seems to be obsolete. But I always used /.is family to create a new folder (so to speak) and /.initial=... to create a new entry. Even /.default would fail without /.initial. Commented Dec 15, 2017 at 23:00

1 Answer 1

1

The answer to this question gave me what I needed to solve this problem. Instead of trying to load the values directly from the key, I store them in intermediate macros. The solution below extends the linked answer to incorporate using .style to initialize all the values for a particular format type and .search also

\documentclass{article}
\usepackage{etoolbox,xparse}
\usepackage{pgfkeys}
\usepackage{multicol}
\usepackage{parskip}
\newcommand{\ActiveFormat}{generic}
\newcommand{\ActiveDomain}{default}
\newcommand{\SectionName}{}
\newcommand{\numcols}{1}
\newcommand{\mysize}{0pt}
\raggedright

\pgfkeys{/stdt/.cd,
    format/.store in=\ActiveFormat,
    domain/.store in=\ActiveDomain,
    name/.store in=\SectionName,
    my-size/.store in=\mysize,
    num-columns/.store in=\numcols,
    % Generic defaults
    generic/default/.cd,
    .style={/stdt/generic/default/.cd,name,num-columns,my-size},
    name/.code=\pgfkeys{/stdt/name=Generic Format},
    num-columns/.code=\pgfkeys{/stdt/num-columns=1},
    my-size/.code=\pgfkeys{/stdt/my-size=18pt},
    % Format one defaults
    /stdt/format-one/default/.cd,
    .style={/stdt/format-one/default/.cd,name,num-columns,my-size},
    name/.code=\pgfkeys{/stdt/name=Format One},
    num-columns/.code=\pgfkeys{/stdt/num-columns=2},
    my-size/.code=\pgfkeys{/stdt/my-size=36pt},
    .search also={/stdt/generic/default},
    % Format one domain one
    /stdt/format-one/domain-one/.cd,
    .style={/stdt/format-one/domain-one/.cd,name,num-columns,my-size},
    name/.code=\pgfkeys{/stdt/name={Format One -- Domain One}},
    .search also={/stdt/format-one/default,/stdt/generic/default},
    % Format two defaults
    /stdt/format-two/default/.cd,
    .style={/stdt/format-two/default/.cd,name,num-columns,my-size},
    name/.code=\pgfkeys{/stdt/name=Format Two},
    num-columns/.code=\pgfkeys{/stdt/num-columns=3},
    .search also={/stdt/generic/default},
    % Format two domain one
    /stdt/format-two/domain-one/.cd,
    .style={/stdt/format-two/domain-one/.cd,name,num-columns,my-size},
    name/.code=\pgfkeys{/stdt/name={Format Two -- Domain One}},
    num-columns/.code=\pgfkeys{/stdt/num-columns=4},
   .search also={/stdt/format-two/default,/stdt/generic/default},
}

\begin{document}
\pgfkeys{/stdt/\ActiveFormat/\ActiveDomain}
\ifnumgreater{\numcols}{1}{\begin{multicols}{\numcols}}{}
    Values with \ActiveFormat/\ActiveDomain:

    Name: \SectionName

    Columns: \numcols

    My Size: \mysize
\ifnumgreater{\numcols}{1}{\end{multicols}}{}

\pgfkeys{/stdt/.cd,format=format-one,domain=default,\ActiveFormat/\ActiveDomain}
\ifnumgreater{\numcols}{1}{\begin{multicols}{\numcols}}{}
    Switched to \ActiveFormat/\ActiveDomain.

    Name: \SectionName

    Columns: \numcols

    My Size: \mysize
\ifnumgreater{\numcols}{1}{\end{multicols}}{}

\pgfkeys{/stdt/.cd,format=format-one,domain=domain-one,\ActiveFormat/\ActiveDomain}
\ifnumgreater{\numcols}{1}{\begin{multicols}{\numcols}}{}
    Switched to \ActiveFormat/\ActiveDomain.

    Name: \SectionName

    Columns: \numcols

    My Size: \mysize
\ifnumgreater{\numcols}{1}{\end{multicols}}{}

\pgfkeys{/stdt/.cd,format=format-two,domain=domain-one,\ActiveFormat/\ActiveDomain}
\ifnumgreater{\numcols}{1}{\begin{multicols}{\numcols}}{}
    Switched to \ActiveFormat/\ActiveDomain.

    Name: \SectionName

    Columns: \numcols

    My Size: \mysize
\ifnumgreater{\numcols}{1}{\end{multicols}}{}

\pgfkeys{/stdt/.cd,format=format-two,domain=default,\ActiveFormat/\ActiveDomain}
\ifnumgreater{\numcols}{1}{\begin{multicols}{\numcols}}{}
    Switched to \ActiveFormat/\ActiveDomain.

    Name: \SectionName

    Columns: \numcols

    My Size: \mysize
\ifnumgreater{\numcols}{1}{\end{multicols}}{}
\end{document}

Results:

test getting values with and without explicit keys set

You must log in to answer this question.

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