15

I would like to define a key, let's call it mystyle, which has several subkeys, like mystyle/A and mystyle/B, and I'd like to be able to choose which of these subkeys to set by calling mystyle=A, mystyle=B, or mystyle={A,B}.

That's easy enough if I need to set just one of the subkeys, it can be accomplished using something like this:

\tikzset{
    mystyle/A/.style=draw,
    mystyle/B/.style={fill=yellow},
    mystyle/.style={
        mystyle/#1
    }
}

That allows me to say \tikz \node [mystyle={B}] {Test};, and the node will be filled in yellow. However, if I try \tikz \node [mystyle={A,B}] {Test};, I get the error I do not know the key '/tikz/B'.

I tried defining the keys as follows instead

\tikzset{
    mystyle/A/.style=draw,
    mystyle/B/.style={fill=yellow},
    mystyle/.style={
        mystyle/.cd,
        #1
    }
}

However, this doesn't work, because while the change of key directory (mystyle/.cd) fixed the problem of pgfkeys not being able to find mystyle/B, I now get the following error message: I do not know the key '/tikz/mystyle/draw'.

What to do?

MWE:

\documentclass{article}
\usepackage{tikz}
\begin{document}
\tikzset{
    mystyle/A/.style=draw,
    mystyle/B/.style={fill=yellow},
    mystyle/.style={
        mystyle/.cd,
        #1
    }
}

\tikz \node [mystyle={A,B}] {Test};
\end{document} 
0

2 Answers 2

12

How does a .style handler work? It is basically a .code handler (as everything is in the end anyway), the code that is saved is \pgfkeysalso{#1}.

The style A expands to \pgfkeysalso{draw} and B to \pgfkeysalso{fill=yelow}.

The manual says for \pgfkeysalso:

This command has execatly the same effect as \pgfkeys, only the default path is not modified before or after the keys are being set.

The “default path” is the one you’re on currently (with \tikzset this is /tikz). The .cd handler however changes this default path to /tikz/mystyle. A key that uses \pgfkeysalso then tries to find draw and fill in this path and fails.

The rather complex handler .search also installs a .unknown code that tries to use unrecognized keys to apply in the given paths. (In a similar way do /pgf keys work in /tikz.) percusse already uses this in his solution and it is probably the most convenient approach.

The problem though is that you must use those keys only in the /tikz/mystyle path. You can’t use them in other paths without setting the default path to /tikz or /tikz/mystyle. For example you can’t do \pgfkeys{/tikz/mystyle/A} because as the key is not called from the /tikz/mystyle path the installed .search also won’t be checked. This may seem as an unrealistic example (and it probably is) but for other styles that do not change the properties of the whole path like for example insert path this is important as a key containing such styles may be used anywhere to set exactly those properties.

The most robust way would be to save the styles with /tikz (and you know they are used as /tikz key and nothing else):

\tikzset{
    mystyle/A/.style=/tikz/draw,
    mystyle/B/.style={/tikz/fill=yellow}
}

If you want the styles to behave as if they would be used in the default /tikz path you could use a .tikz handler that saves the key as a \tikzset (as opposed to a \pgfkeysalso). A simple definition like

\pgfkeys{/handlers/.tikz/.code=%
  \pgfkeys{\pgfkeyscurrentpath/.code=\tikzset{#1}}}

makes it possible to use it as

\tikzset{
    mystyle/A/.tikz={draw},
    mystyle/B/.tikz={fill=yellow}}

In a way, this is also my solution for the mystyle switch key.

Again, a mystyle/.cd (which also only work if the previous path was /tikz) changes the path even for keys you would want to use after mystyle={A,B}. While those are most likely again /tikz keys they don’t need to be. You could have used /tikz/mystyle={A,B} from any PGF keys path without wanting to go back to /tikz. Similar to a .tikz handler I would define the mystyle key as:

\tikzset{
    mystyle/A/.tikz={draw},
    mystyle/B/.tikz={fill=yellow},
    mystyle/.code=\pgfqkeys{/tikz/mystyle}{#1}
}

The manual even says to \pgfkeysalso that “[c]hanging the default path inside a \pgfkeyalso is dangerous, so use with care”.


Similar solutions are used by TikZ naturally, for example the decoration key (which is actually a /pgf key):

\pgfkeys{%
  /pgf/decoration/.code={\pgfkeys{/pgf/decoration/.cd,#1}}}

This for example makes it possible to use decoration={<something>} from \pgfset (e.g. the /pgf path) and also from \tikzset without the restraint to go back to /pgf or /tikz. (It would be save though to go back to /tikz as /pgf is searched from there anyway but for a pure pgf solution this would fail.)

Code

\documentclass[tikz,convert=false]{standalone}
\pgfkeys{/handlers/.tikz/.code=%
  \pgfkeys{\pgfkeyscurrentpath/.code=\tikzset{#1}}}

\tikzset{
    mystyle/A/.tikz={draw},
    mystyle/B/.tikz={fill=yellow},
    mystyle/.code=\pgfqkeys{/tikz/mystyle}{#1}
}

\begin{document}
\begin{tikzpicture}
\node [mystyle={A,B}] {Test};
\end{tikzpicture}
\end{document}

Another solution you might be interested in is the following.

This avoids changing the default path and relies on using mystyle={…} when the default path is /tikz. You can know say /.style={</tikz options>} and use mystyle={…} without using an additional handler, the need to specify /tikz somehow and somewhere. All it needs is an auxiliary key to use with a /.list. (Or you specify the /.list in your code directly while using your original definition (mystyle/.style={mystyle/#1}): mystyle/.list={A,B}.

Though, if you know that the keys you use are TikZ keys, save them as such (with /.tikz or /tikz/).

Code

\documentclass[tikz,convert=false]{standalone}

\tikzset{
    mystyle/A/.style={draw},
    mystyle/B/.style={fill=yellow},
    mystyle/.style={@mystyle/.list={#1}},
    @mystyle/.style={/tikz/mystyle/#1}% auxiliary
}

\begin{document}
\begin{tikzpicture}
\node [mystyle={A,B}] {Test};
\end{tikzpicture}
\end{document}
2
  • Excellent, this really clears up a lot of things! The .list approach especially will come in very handy. Thank you!
    – Jake
    Commented Jul 28, 2013 at 2:55
  • \pgfkeys{/tikz/mystyle/A} shouldn't be used anyway by design. or you explicitly change directory otherwise you will have problems with insert path too. For those there is an additional filter mechanism is given. Otherwise that would cause more trouble than it solves.
    – percusse
    Commented Jul 29, 2013 at 8:07
10

You can declare alternative locations to look for the unknown keys via /.search also handler. This is also done occasionally in pgfplots code to collect TikZ based keys for paths (though it has a full-blown handler config in action).

\documentclass[tikz]{standalone}
\tikzset{
    mystyle/A/.style={draw},
    mystyle/B/.style={fill=yellow},
    mystyle/.search also={/tikz,/pgf},
    mystyle/.style={
        mystyle/.cd,
        #1,
        /tikz/.cd
    }
}

\begin{document}
\begin{tikzpicture}
\node [mystyle={A,B}] {Test};
\end{tikzpicture}
\end{document}

enter image description here

You must log in to answer this question.

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