8

The following minimal working example shows the problem. The mainmacro is an example of what I need; here I want to use my keys and keys from tikz (like draw,color,shape). The result is fine only if I use shape = circle. circle is not enough. Idem if I want the color red. color = red is obligatory. What is wrong in my code ? Do you know a better way to achieve what I want ?

\documentclass[a4paper]{article}
\usepackage{tikz}
\usetikzlibrary{calc}

\makeatletter
\pgfkeys{/mykeys/.cd,
d/.code                     = {\def\macro@d{#1}},   
a/.code                     = {\def\macro@a{#1}},
/mykeys/.unknown/.code      = {\let\searchname=\pgfkeyscurrentname
                                               \pgfkeysalso{\searchname/.try=#1,
                                                    /tikz/\searchname/.retry=#1}
                              }    
        }  
\def\mainmacro{\pgfutil@ifnextchar[{\main@macro}{\main@macro[]}}
\def\main@macro[#1](#2)#3{%
\begingroup
  \pgfkeys{mykeys/.cd,
           d = 1,
           a = 45}
  \pgfqkeys{/mykeys}{#1}
  \path (#2) --+(\macro@a:\macro@d) node[/mykeys/.cd,#1] {#3};
\endgroup
}
\makeatother

\begin{document} 

    \begin{tikzpicture}
        \draw (0,0) --( 1,1) coordinate (a) ;
        \mainmacro[a=0,d=12pt,draw,shape=circle](a){label 1}    
        \mainmacro[d=2cm,draw,circle](a){label 2}       
    \end{tikzpicture}

\end{document}

enter image description here

Update

\documentclass[a4paper]{article}
\usepackage{tikz}
\usetikzlibrary{calc}

\makeatletter
\pgfkeys{/mykeys/.cd,
d/.store in                 = {\macro@d},   
a/.store in                 = {\macro@a},
/mykeys/.unknown/.code      = {\let\searchname=\pgfkeyscurrentname
                                               \pgfkeysalso{\searchname/.try=#1,
                                                    /tikz/\searchname/.retry=#1,
                                                    /tikz/.cd,#1}}}  

\def\mainmacro{\pgfutil@ifnextchar[{\main@macro}{\main@macro[]}}
\def\main@macro[#1](#2){%
\begingroup
\pgfkeys{mykeys/.cd,
           d = 1,
           a = 1}
\pgfqkeys{/mykeys}{#1}

\draw[/mykeys/.cd,#1] (\macro@a,\macro@a) circle (\macro@d) --++(1,1) node[#1] {label};% 
\endgroup
}
\makeatother

\begin{document} 
    \begin{tikzpicture}
        \draw (0,0) -- (5,0) ;
        \mainmacro[red,ultra thick](a)       
    \end{tikzpicture}

\end{document}

enter image description here

The trick works for the node but not with \draw perhaps I'm wrong somewhere in my code

11
  • I think /.store in handler is better here.
    – percusse
    Mar 16, 2016 at 9:36
  • @percusse why do you think it's better ? there is a document about this ? I have some difficulties with the pgfmanual about this ... a complete example would be better Mar 16, 2016 at 13:05
  • I guess it handles the current scope better but I don't have any rigorous argument. It might be just a pschological preference.
    – percusse
    Mar 16, 2016 at 13:07
  • @percusse no psch. because with your code 1. I get an error if I use .code instead of .store in and I am not happy not understand why ! Perhaps it's a new question interesting ? Mar 16, 2016 at 14:06
  • I think I know why the order matters. I'll check at home.
    – percusse
    Mar 30, 2016 at 14:54

2 Answers 2

5

This happens because shape, color names (and some other stuff) are not keys. In other words, when you write red or diamond it doesn't have a dedicated key for such things. Hence, it goes through the TikZ machinery of searchname/.retry but because no such key is there it fails. Instead /tikz/.unknown/.code is much more comprehensive and sophisticated. For example

{%
        % Ok, second chance: This might be an arrow specification:
        \expandafter\pgfutil@in@\expandafter-\expandafter{\tikz@key}%
        \ifpgfutil@in@%
          % Ah, an arrow spec!
          \expandafter\tikz@processarrows\expandafter{\tikz@key}%
        \else%
          % Ok, third chance: A shape!
          \expandafter\ifx\csname pgf@sh@s@\tikz@key\endcsname\relax%
            \pgfkeys{/errors/unknown key/.expand
            once=\expandafter{\expandafter/\expandafter t\expandafter
            i\expandafter k\expandafter z\expandafter/\tikz@key}{##1}}%
          \else%
            \edef\tikz@shape{\tikz@key}%
          \fi%
        \fi%
      }%

I can think of two ways to do this:

  1. You can in the end of the key trial, pass to TikZ explicitly and let it decide what to do. Hence you add the following

    \pgfkeys{/mykeys/.cd,
    d/.store in            = {\macro@d},   
    a/.store in            = {\macro@a},
    /mykeys/.unknown/.code = {\let\searchname=\pgfkeyscurrentname%
                              \pgfkeysalso{\searchname/.try=#1,
                              /tikz/\searchname/.retry=#1,
                              /tikz/.cd,#1}% <--- Now invoked under TikZ
                             }
    }  
    

    Here, we explicitly trigger TikZ unknown machinery since the family is TikZ now. Hence goes through all the shape name checks and so on.

  2. Write your own /.unknown/.code handler to account for known names. For example in Make a PGFkeys path take precedence over another path for a whole scope there are a few examples.

2
  • I need to study your answer but I agree with you for the diagnostic about the keys. The two ways proposed are interesting. Mar 16, 2016 at 12:23
  • Your code works with a node but with my new code \draw ... Mar 30, 2016 at 15:08
4

Here is two simple solutions.

First solution (the better ?)

You may use the /.search also handler (see p.891 of pgfmanual, v3.0.1a):

\documentclass[a4paper]{article}
\usepackage{tikz}
\usetikzlibrary{calc}

\makeatletter
\pgfkeys{
  /mykeys/.cd,
  d/.code                     = {\def\macro@d{#1}},   
  a/.code                     = {\def\macro@a{#1}},
  /mykeys/.search also={/tikz},
}  
\def\mainmacro{\pgfutil@ifnextchar[{\main@macro}{\main@macro[]}}
\def\main@macro[#1](#2)#3{%
\begingroup
  \pgfkeys{mykeys/.cd,
           d = 1,
           a = 45}
  \pgfqkeys{/mykeys}{#1}
  \path (#2) --+(\macro@a:\macro@d) node[/mykeys/.cd,#1] {#3};
\endgroup
}
\makeatother

\begin{document} 

    \begin{tikzpicture}
        \draw (0,0) --( 1,1) coordinate (a) ;
        \mainmacro[a=0,d=12pt,draw,red,shape=circle](a){label 1}    
        \mainmacro[d=2cm,draw,circle](a){label 2}       
    \end{tikzpicture}

\end{document}

Second solution

You may use the /.forward to handler (see p.890 of pgfmanual, v3.0.1a):

\documentclass[a4paper]{article}
\usepackage{tikz}
\usetikzlibrary{calc}

\makeatletter
\pgfkeys{
  /mykeys/.cd,
  d/.code                     = {\def\macro@d{#1}},   
  a/.code                     = {\def\macro@a{#1}},
  /mykeys/.unknown/.forward to=/tikz/\pgfkeyscurrentname,
}  
\def\mainmacro{\pgfutil@ifnextchar[{\main@macro}{\main@macro[]}}
\def\main@macro[#1](#2)#3{%
\begingroup
  \pgfkeys{mykeys/.cd,
           d = 1,
           a = 45}
  \pgfqkeys{/mykeys}{#1}
  \path (#2) --+(\macro@a:\macro@d) node[/mykeys/.cd,#1] {#3};
\endgroup
}
\makeatother

\begin{document} 
    \begin{tikzpicture}
        \draw (0,0) --( 1,1) coordinate (a) ;
        \mainmacro[a=0,d=12pt,draw,red,shape=circle](a){label 1}    
        \mainmacro[d=2cm,draw,circle](a){label 2}       
    \end{tikzpicture}
\end{document}
4
  • Thanks for the information but I update my question. Nothing works with the new code. Mar 30, 2016 at 14:50
  • @AlainMatthes /.search also works with your new example... Mar 30, 2016 at 14:56
  • Yes great but do you have an explanation because now I'm lost ... Mar 30, 2016 at 14:57
  • The .search also handler more or less wraps/redirects/aliases the .unknown/.code handler. Paul's code is effectively the same at Percusse's. More strictly I think .search also will intercept arguments passed to a key first, if it is specified. if it doesn't catch the arguments pgf will degrade to .unkown/.code, if that exists (is specified), else pgf will error out. I'm not sure how .search also differs from .forward to.
    – Carel
    Jul 1, 2016 at 23:18

You must log in to answer this question.

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