9

mystyle/.style= {...} is perhaps used ?

I would like to know how to verify if the style is really defined and in this case, I would like to know if the style is empty ?

Is it possible to use \pgfkeysifdefined to get the first result ?

Alain Matthes
  • 95,075

3 Answers3

8

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!

Ben Lerner
  • 7,082
7

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}
David Carlisle
  • 757,742
Ryan Reich
  • 37,958
5
\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}
cjorssen
  • 10,032
  • 4
  • 36
  • 126
  • Thanks It was exactly what I needed. I made another test with \begin{tikzpicture}[other style/.style={thick}] and the result is fine ! – Alain Matthes Feb 28 '12 at 16:03
  • Be careful with this code -- if I understand the pgf manual correctly (p485), there are several ways .@cmd can be defined, not merely as a side-effect of setting .style. So \pgfkeysifstyledefined may return false positives (and ...empty may return false negatives). A more robust solution, I think (but have not attempted), would redefine /handlers/.style/.@cmd to record the key names it defines somewhere in a parallel key tree (similar to how trace-pgfkeys works, I think) and then checks that. But that's complicated and probably more work than you need for the common case here... – Ben Lerner Feb 28 '12 at 16:45
  • @BenLerner: trace-pgfkeys does not use a parallel tree, unless you mean that it redefines the internals of pgfkeys, which is true. Though I did write an answer, also dealing with styles in a different way (http://tex.stackexchange.com/a/43150/575) that does use a parallel tree for a similar purpose. – Ryan Reich Feb 28 '12 at 19:57
  • @RyanReich, I guess I was thinking of that :) – Ben Lerner Feb 28 '12 at 20:07