1

I am trying to collect into a command called \drawtasks a series of \drawtask commands that call TikZ. Every time I call \wptask, the command should increment a counter and append to \drawtasks a call to \drawtask with the current value counter. Unfortunately, when I finally call \drawtasks, I always get the last counter value. I've tried out solutions proposed in similar questions on here (e.g., here, but it just doesn't work: it seems that my problem is different in that I want to append a call to another command which should not be expanded. Here is an example:

\documentclass{article}
\usepackage{tikz}

\newcommand{\drawtask}[4]{
    \node[anchor=west] at (-2,-0.5 * #1 - 0.25) {Task #2} ;
    \filldraw[fill=green!20,draw=green!50!black] (#3 / 3,-0.5 * #1 - 0.15) rectangle (#4 / 3,-0.5 * #1 - 0.35) ;

}
\newcounter{wptaskcounter}
\setcounter{wptaskcounter}{0}
\newcommand{\drawtasks}{}
\makeatletter
\newcommand{\appendwptask}[4]{%
    \protected@edef\@tempa{#1}%
    \expandafter\g@addto@macro\expandafter\drawtasks\expandafter{%
        \drawtask{\@tempa}{#2}{#3}{#4}%
    }%
}
\makeatother
\newcommand{\wptask}[3]{%
    \appendwptask{\thewptaskcounter}{#1}{#2}{#3}%
    \stepcounter{wptaskcounter}%
}

\wptask{1.1}{0}{3}
\wptask{1.2}{3}{6}
\wptask{1.3}{6}{9}

\begin{document}

\begin{tikzpicture}
    \drawtasks
\end{tikzpicture}

\end{document}

I'm really confused with the expansion rules of LaTeX and would really appreciate some help! Thanks in advance.

  • Don't you get a compilation error? – cfr Oct 7 '16 at 1:25
  • The funny thing is that I do if I compile it like this, but not if I replace \documentclass{article} with my own class that I created for the project. This seems to happen even if I add all the other packages I used in the class. And the class actually modifies "article", so I'm baffled by this behaviour too! – Boris Oct 7 '16 at 6:07
  • You're expanding \drawtask before the {} whereas you want to expand \@tempa and have \drawtask not be expanded, don't you? – cfr Oct 7 '16 at 14:51
  • I see. Would you mind telling me how exactly to achieve this? (I'm not really completely clear with how \expandafter works.) – Boris Oct 7 '16 at 14:53
  • I'm useless with expansion - I don't understand it at all. But the \expandafter just skips the next thing, which is the {, and expands the thing after, which is \drawtask. At least, that's what egreg says. I asked in a comment on the answer you linked to. – cfr Oct 7 '16 at 16:00
2

Based on egreg's comments on the answer you linked in the question, you might do it this way.

I always read \expandafter<token 1><token 2> as meaning something like come back and expand <token 1> after expanding <token 2> i.e. expand this next thing after the thing following it.

However, it seems instead to mean expand <token 2> next and then come back to <token 1> i.e. expand the thing after the next thing next.

In that case, what you want within the \drawtasks is for TeX to consider \drawtask only after looking at \@tempa. So you might try

\expandafter\drawtask\expandafter{\@tempa ...

which says to look at \drawtask only after expanding (looking at \{ only after expanding \@tempa).

... er, I think. However, I'm almost certainly wrong.

Nonetheless,

\newcommand{\appendwptask}[4]{%
    \protected@edef\@tempa{#1}%
    \expandafter\g@addto@macro\expandafter\drawtasks\expandafter{%
        \expandafter\drawtask\expandafter{\@tempa}{#2}{#3}{#4}%
    }%
}

seems to work, I think.

accumulated tasks

\documentclass{article}
\usepackage{tikz}

\newcommand{\drawtask}[4]{
    \node[anchor=west] at (-2,-0.5 * #1 - 0.25) {Task #2} ;
    \filldraw[fill=green!20,draw=green!50!black] (#3 / 3,-0.5 * #1 - 0.15) rectangle (#4 / 3,-0.5 * #1 - 0.35) ;

}
\newcounter{wptaskcounter}
\setcounter{wptaskcounter}{0}
\newcommand{\drawtasks}{}
\makeatletter
\newcommand{\appendwptask}[4]{%
    \protected@edef\@tempa{#1}%
    \expandafter\g@addto@macro\expandafter\drawtasks\expandafter{%
        \expandafter\drawtask\expandafter{\@tempa}{#2}{#3}{#4}%
    }%
}
\makeatother
\newcommand{\wptask}[3]{%
    \appendwptask{\thewptaskcounter}{#1}{#2}{#3}%
    \stepcounter{wptaskcounter}%
}

\wptask{1.1}{0}{3}
\wptask{1.2}{3}{6}
\wptask{1.3}{6}{9}

\begin{document}
\begin{tikzpicture}
    \drawtasks
\end{tikzpicture}
\end{document}
2

Rather than doing it this way I would make \wptask generate a comma separated list and then have \drawtasks loop over the list using tikz/pgf's \foreach command.

Doing it this way, your MWE produces:

enter image description here

Here's the code:

\documentclass{article}
\usepackage{tikz}

\let\wptasklist\relax% will become a comma separate list of the task parameters
\newcommand{\wptask}[3]{%  \addtask might be a better name:)
   \ifx\wptasklist\relax\def\wptasklist{#1/#2/#3}% first task
   \else\xdef\wptasklist{\wptasklist,#1/#2/#3}% add a task
   \fi%
}

\newcommand\drawtasks{% loop over \wptasklist and draw the tasks
  \begin{tikzpicture}
     \foreach \a/\b/\c [count=\n] in \wptasklist {% loop over task list
        \node[anchor=west] at (-2,-0.5 * \n - 0.25) {Task \a};
        \filldraw[fill=green!20,draw=green!50!black] 
                 (\b/3,-0.5*\n-0.15) rectangle (\c/3,-0.5*\n-0.35);
     }
  \end{tikzpicture}
}

\wptask{1.1}{0}{3}
\wptask{1.2}{3}{6}
\wptask{1.3}{6}{9}

\begin{document}

    \drawtasks

\end{document}

Hopefully the code is fairly self-explanatory. Notice that there are no expansion issues, except for the use of \xdef to add to the task list, and there is no need for a counter because you can use [count=\n] as part of the \foreach loop to determine the "task number".

There is one caveat, which doesn't seem to apply in your use-case: any of the parameters to \wptask that contain a slash / will need to be put inside braces {.../...}.

EDIT

Here is a second solution that is closer to the OP's MWE. It uses \eappto from the etoolbox package, which is a robust and general implementation of the \appendwptask macro in the question, together with \noexpand. For me at least, this gives a is more intuitive way of coping with these expansion issues. The output is the same as above.

\documentclass{article}
\usepackage{etoolbox}
\usepackage{tikz}

\newcommand{\drawtask}[4]{
    \node[anchor=west] at (-2,-0.5 * #1 - 0.25) {Task #2} ;
    \filldraw[fill=green!20,draw=green!50!black] (#3 / 3,-0.5 * #1 - 0.15) rectangle (#4 / 3,-0.5 * #1 - 0.35) ;

}
\newcounter{wptaskcounter}% set to 0 by default
\newcommand{\drawtasks}{}
\newcommand{\wptask}[3]{%
    \eappto\drawtasks{\noexpand\drawtask{\thewptaskcounter}{#1}{#2}{#3}}% append to \drawtasks
    \stepcounter{wptaskcounter}%
}

\wptask{1.1}{0}{3}
\wptask{1.2}{3}{6}
\wptask{1.3}{6}{9}

\begin{document}
  \begin{tikzpicture}
    \drawtasks
  \end{tikzpicture}
\end{document}

If you want to "accumulate many things" then using a key-val system like pgfkeys might be a better option...well, except for the initial learning curve:)

  • Thank you, this works well. However, I was going to accumulate other things than just the \drawtask command; for example, I'd like to add separators and so on. Thus, the structure of things in the list won't be homogeneous, which is why accumulating through a command seemed more appropriate. Thanks nevertheless, it's a nice solution! – Boris Oct 7 '16 at 6:11
  • @Boris It is difficult to anticipate what you want to do if it is not illustrated by your minimal working example. You should either extend your MWE or ask another question. Btw, the "correct" way to thank is to up-vote and assign green ticks when appropriate:) – Andrew Oct 7 '16 at 7:16
  • @Boris You might find it helpful t look at the etoolbox as it provides a number of "tools" to make expansion easier. An approach using pgf keys is also worth considering. – Andrew Oct 8 '16 at 12:29
1

You can use \unexpanded:

\documentclass{article}
\usepackage{tikz}

\providecommand{\expandonce}[1]{%
  \unexpanded\expandafter{#1}%
} % etoolbox already provides this

\newcommand{\drawtask}[4]{
  \node[anchor=west] at (-2,-0.5 * #1 - 0.25) {Task #2} ;
  \filldraw[fill=green!20,draw=green!50!black]
    (#3 / 3,-0.5 * #1 - 0.15) rectangle (#4 / 3,-0.5 * #1 - 0.35) ;
}
\newcounter{wptaskcounter}
\setcounter{wptaskcounter}{0}
\newcommand{\drawtasks}{}

\newcommand{\appendwptask}[4]{%
  \edef\drawtasks{%
    \expandonce{\drawtasks}%
    \expandonce{\drawtask{#1}{#2}{#3}{#4}}%
  }%
}
\newcommand{\wptask}[3]{%
  \expandafter\appendwptask\expandafter{\the\value{wptaskcounter}}{#1}{#2}{#3}%
  \stepcounter{wptaskcounter}%
}

\wptask{1.1}{0}{3}
\wptask{1.2}{3}{6}
\wptask{1.3}{6}{9}

\begin{document}

\begin{tikzpicture}
\drawtasks
\end{tikzpicture}

\end{document}

In this way, \expandonce{\drawtasks} will only expand once \drawtasks, so to get its previous value; similarly, \expandonce{\drawtask{...}{...}{...}{...}} will perform the first level expansion of \drawtask, leaving the replacement text (with parameters replaced by the arguments). You also need to expand the value of the counter.

enter image description here

A much cleaner version can be obtained by using expl3 (via xparse for the interface):

\documentclass{article}
\usepackage{tikz}
\usepackage{xparse}

\newcommand{\drawtask}[4]{
  \node[anchor=west] at (-2,-0.5 * #1 - 0.25) {Task #2} ;
  \filldraw[fill=green!20,draw=green!50!black]
    (#3 / 3,-0.5 * #1 - 0.15) rectangle (#4 / 3,-0.5 * #1 - 0.35) ;
}

\ExplSyntaxOn
\tl_new:N \g_boris_drawtasks_tl
\int_new:N \g_boris_wptask_int

\NewDocumentCommand{\drawtasks}{}
 {
  \tl_use:N \g_boris_drawtasks_tl
 }

\NewDocumentCommand{\wptask}{mmm}
 {
  \boris_append_wptask:fnnn
    { \int_to_arabic:n { \g_boris_wptask_int } }
    { #1 }
    { #2 }
    { #3 }
  \int_gincr:N \g_boris_wptask_int
 }

\cs_new_protected:Nn \boris_append_wptask:nnnn
 {
  \tl_gput_right:No \g_boris_drawtasks_tl
   {
    \drawtask{#1}{#2}{#3}{#4}
   }
 }
\cs_generate_variant:Nn \boris_append_wptask:nnnn { f }
\ExplSyntaxOff

\wptask{1.1}{0}{3}
\wptask{1.2}{3}{6}
\wptask{1.3}{6}{9}

\begin{document}

\begin{tikzpicture}
\drawtasks
\end{tikzpicture}

\end{document}

We take care of getting the expanded version of the counter's value by defining a variant f (full expansion).

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.