How to know the status of a style? defined? empty?

It appears not -- .style is a handler that sets .code, which in turn is a handler that sets the internal .@cmd attribute. Using Ryan Reich's incredibly cool trace-pgfkeys package (from this question), the following code

\documentclass{article}
\usepackage{pgfkeys}
\usepackage[tracemacros]{trace-pgfkeys}
\pgfkeystracelevel{verbose}
\begin{document}
\makeatletter

\typeout{Setting /test1/.style}
\pgfkeys{/test1/.style={shape=rectangle}}
\typeout{Checking ifdefine /test1/.style}
\pgfkeysifdefined{/test1/.style}{True}{False}
\typeout{Checking ifdefine /test1/.code}
\pgfkeysifdefined{/test1/.code}{True}{False}
\typeout{Checking ifdefine /test1/.@cmd}
\pgfkeysifdefined{/test1/.@cmd}{True}{False}


\typeout{Checking ifdefine /test-missing/.style}
\pgfkeysifdefined{/test-missing/.style}{True}{False}
\typeout{Checking ifdefine /test-missing/.code}
\pgfkeysifdefined{/test-missing/.code}{True}{False}
\typeout{Checking ifdefine /test-missing/.@cmd}
\pgfkeysifdefined{/test-missing/.@cmd}{True}{False}

\typeout{Setting /test2/.style to nothing}
\pgfkeys{/test2/.style=}

\pgfkeysgetvalue{/test2/.@cmd}{\test}
\ifx\test\relax Empty \else Not empty \fi
\end{document}

produces the following document:

False False True
False False False
Not empty

and a lot of useful debugging output, the following trace for setting /test1/.style:

Setting /test1/.style
[trace-pgfkeys]-: Tracing \pgfkeys call.
[trace-pgfkeys]-+ Key/value list: (/test1/.style={shape=rectangle})
[trace-pgfkeys]-+ Current key-value: (/test1/.style={shape=rectangle})
[trace-pgfkeys]-+ \pgfkeyscurrentkey (given): /test1/.style
[trace-pgfkeys]-: Full key; no path added.
[trace-pgfkeys]-> \pgfkeyscurrentkey : /test1/.style
[trace-pgfkeys]-+ \pgfkeyscurrentvalue : (shape=rectangle)
[trace-pgfkeys]-: Case one: key code?
[trace-pgfkeys]-: Case two: key value?
[trace-pgfkeys]-: Case three: key unknown. Splitting the path.
[trace-pgfkeys]-+ \pgfkeyscurrentpath (/test1)
[trace-pgfkeys]-+ \pgfkeyscurrentname (.style)
[trace-pgfkeys]-: Checking whether a handler is defined.
[trace-pgfkeys]-+ Executing all handlers.
[trace-pgfkeys]-: Tracing \pgfkeysgetvalue .
[trace-pgfkeys]-: Value: (\long macro:#1\pgfeov ->\pgfkeys {\pgfkeyscurrentpath /.code=\pgfkeysalso {#1}})
[trace-pgfkeys]--+ Storing key value in \pgfkeys@code 
[trace-pgfkeys]--> Storing: /handlers/.style/.@cmd 
[trace-pgfkeys]--+ Done storing.
[trace-pgfkeys]-: Handler code:
[trace-pgfkeys]-: \long macro:#1\pgfeov ->\pgfkeys {\pgfkeyscurrentpath /.code=\pgfkeysalso {#1}}
[trace-pgfkeys]--+ Executing handler.
[trace-pgfkeys]--> Handler: /handlers/.style
[trace-pgfkeys]---: Tracing \pgfkeys call.
[trace-pgfkeys]---+ Key/value list: (\pgfkeyscurrentpath /.code=\pgfkeysalso {shape=rectangle})
[trace-pgfkeys]---+ Current key-value: (\pgfkeyscurrentpath /.code=\pgfkeysalso {shape=rectangle})
[trace-pgfkeys]---+ \pgfkeyscurrentkey (given): /test1/.code
[trace-pgfkeys]---: Full key; no path added.
[trace-pgfkeys]---> \pgfkeyscurrentkey : /test1/.code
[trace-pgfkeys]---+ \pgfkeyscurrentvalue : (\pgfkeysalso {shape=rectangle})
[trace-pgfkeys]---: Case one: key code?
[trace-pgfkeys]---: Case two: key value?
[trace-pgfkeys]---: Case three: key unknown. Splitting the path.
[trace-pgfkeys]---+ \pgfkeyscurrentpath (/test1)
[trace-pgfkeys]---+ \pgfkeyscurrentname (.code)
[trace-pgfkeys]---: Checking whether a handler is defined.
[trace-pgfkeys]---+ Executing all handlers.
[trace-pgfkeys]---: Tracing \pgfkeysgetvalue .
[trace-pgfkeys]---: Value: (\long macro:#1\pgfeov ->\pgfkeysdef {\pgfkeyscurrentpath }{#1})
[trace-pgfkeys]----+ Storing key value in \pgfkeys@code 
[trace-pgfkeys]----> Storing: /handlers/.code/.@cmd 
[trace-pgfkeys]----+ Done storing.
[trace-pgfkeys]---: Handler code:
[trace-pgfkeys]---: \long macro:#1\pgfeov ->\pgfkeysdef {\pgfkeyscurrentpath }{#1}
[trace-pgfkeys]----+ Executing handler.
[trace-pgfkeys]----> Handler: /handlers/.code
[trace-pgfkeys]----: Tracing \pgfkeysdef .
[trace-pgfkeys]----: Code: (\pgfkeysalso {shape=rectangle})
[trace-pgfkeys]-----+ Defining key code.
[trace-pgfkeys]-----> New code: /test1
[trace-pgfkeys]-----: Tracing \pgfkeyslet .
[trace-pgfkeys]------+ Assigning key from \pgfkeys@temp 
[trace-pgfkeys]------> Assigning: /test1/.@cmd
[trace-pgfkeys]------+ Done assigning.
[trace-pgfkeys]-----+ Done defining.
[trace-pgfkeys]----+ Done defining.
[trace-pgfkeys]---+ Execution finished.
[trace-pgfkeys]--+ Last key processed.
[trace-pgfkeys]-+ Execution finished.
[trace-pgfkeys]+ Last key processed.

Note that /.style has a /.@cmd that in turn sets /.code, which itself has a /.@cmd that finally sets the /test1/.@cmd attribute directly. Hope that helps!


Here is my take on this answer. Basically, setting a .style has the effect of setting .@cmd to exactly the code \pgfkeysalso{<keys>}. I take this to be the definition of "is a style", since there is no way to check that this kind of code wasn't produced by setting .code directly, but then, if it quacks like a duck...

I provide three user commands:

  • \pgfkeysifstyle{<full key>}{<true>}{<false>}, which acts based on whether the key is a style in the sense above.

  • \lastpgfkeysstyle, which stores the contents of the style if the key is a style. If it's not, don't use this.

  • \pgfkeysifstyleempty (same args), which acts based on whether that style is empty. If it's not a style, I go with "true" since otherwise you might want to use \lastpgfkeysstyle.

The code:

\documentclass{article}
\usepackage{filecontents}

\begin{filecontents*}{pgfkeys-ifstyle.sty}
 \RequirePackage{etoolbox}% for \ifstrequal, \ifdefempty

 \def\lastpgfkeysstyle{}
 % Checks if #1 = \pgfkeysalso{...} and, if so, puts the argument
 % in \lastpgfkeysstyle
 \def\@ifgetpgfkeysalso#1\pgfkeysalso#2#3\@nil{%
  \ifstrequal{#1.#3}{.\pgfkeysalso{}}% \pgfkeysalso{#2} is the only thing there
   {\def\lastpgfkeysstyle{#2}\@firstoftwo}
   {\@secondoftwo}%
 }

 % So I don't need to write this again
 \def\expandaftertwice{\expandafter\expandafter\expandafter}

 \def\pgfkeysifstyle#1{%
  % "Expand" the key's .@cmd until its code shows
  % Even if there is no .@cmd, we get #1\pgfeov, which will not screw us up.
  \toks0=\expandaftertwice{\csname pgfk@#1/.@cmd\endcsname##1\pgfeov}%
  \expandafter\@ifgetpgfkeysalso\the\toks0 \pgfkeysalso{}\@nil% That space is very important!
 }
 \def\pgfkeysifstyleempty#1{%
  \pgfkeysifstyle{#1}
   {\ifdefempty{\lastpgfkeysstyle}}
   {% We count a non-style as empty, since otherwise you might use \lastpgfkeysstyle
    \PackageWarning{pgfkeys-ifstyle}
     {In \noexpand\pgfkeysifstyleempty\MessageBreak
      The key #1 is not defined as a style!\MessageBreak}
    \@firstoftwo
   }%
 }
\end{filecontents*}

\usepackage{pgfkeys-ifstyle}
\usepackage{pgfkeys,pgffor}

\begin{document}
 \pgfkeys{
  /key 1/.style = {x, y, z},
  /key 2/.code = {Not a style #1},
  /key 3/.initial = {Not even a code},
  /key 4/.style = {},% Empty style
  /key 5/.code = {\pgfkeysalso{a, b, c} and other stuff},
  /key 6/.code = {\pgfkeysalso{a, b, c}},
 }

 \newcommand*\teststyle[1]{%
  \par\medskip\noindent
  \pgfkeysifstyle{#1}{#1 has a style: \lastpgfkeysstyle}{#1 is not a style}\\
  \pgfkeysifstyleempty{#1}{#1 has an empty style}{#1 does not have an empty style}
 }

 \foreach \n in {1,...,6} {\teststyle{/key \n}}
\end{document}

\documentclass{standalone}
\usepackage{tikz}
\makeatletter
\def\pgfkeysifstyledefined#1#2#3{%
  \pgfkeys@ifcsname pgfk@#1/.@cmd\endcsname#2\else#3\fi}

\def\pgfkeysifstyleempty#1{%
  \expandafter\expandafter\expandafter\pgfkeysifstyleempty@i
  \csname pgfk@#1/.@cmd\endcsname\pgfeov}

\def\pgfkeysifstyleempty@i\pgfkeysalso#1#2#3{%
  \if\relax\detokenize{#1}\relax
    #2%
  \else
    #3%
  \fi}
\makeatother

\begin{document}

\pgfkeysifstyledefined{/tikz/my style}{Defined}{Undefined}

\tikzset{my style/.style = {}}
\pgfkeysifstyledefined{/tikz/my style}{Defined}{Undefined}

\pgfkeysifstyleempty{/tikz/my style}{Empty}{Non empty}

\tikzset{my style/.style = {Test}}
\pgfkeysifstyleempty{/tikz/my style}{Empty}{Non empty}

\end{document}

Tags:

Pgfkeys