7

I'm programming a Calendar in TikZ and I'm running into a problem while trying to display vacations. In the code below, the loop is what I'm trying to do, because it's more dynamic and I don't have to hard code it inside the actual \calendar[] function. The commented out code works well. All the days that are between the dates are grayed out. With the loop, nothing happens. There is no error message (which is good I think), but there is also no grayed out cell, which is bad...

Why doesn't this work and how could I get it working?


Calendar

\documentclass[landscape, a4paper, ngerman, 10pt]{article}
\usepackage[utf8]{inputenc}
\usepackage[ngerman]{babel}
\usepackage[T1]{fontenc}
\usepackage{tikz, xparse}
\usepackage{lmodern}
\usepackage{translator}                 % German Month and Day names
\usepackage{fancyhdr}                   % header and footer
\usepackage{color}                      % Colors
\usepackage[
  headheight = 11ex,
  margin=.5cm,
  top = 3.2cm,
  nofoot,
]{geometry}

\usetikzlibrary{calc, calendar}
\renewcommand*\familydefault{\sfdefault}

% ------------------------------------------------------- 
%
% User defined
%
% -------------------------------------------------------

% Input the variables.tex
% Defines the user variables
\input{variables}

% Header
\renewcommand{\headrulewidth}{0pt}
\fancyhf{}
\lhead{%
    \fontsize{60}{70}\selectfont\bfseries{%
        SS \currentyear
    }
}

% ------------------------------------------------------- 
%
% Calendar
%
% -------------------------------------------------------

\begin{document}
    \pagestyle{fancy}
    \centering

    % ------------------------------------------------------- 
    %
    % Summersemester
    % April to September
    %
    % ------------------------------------------------------- 
    \begin{tikzpicture}[every day/.style={anchor = north}]
        \calendar[
            dates = \currentyear-04-01 to \currentyear-09-30,
            name = cal,
            day yshift = 3em,
            day code = {
                \node[name = \pgfcalendarsuggestedname, every day, minimum height = .53cm, text width = 4.4cm, draw = gray] {\tikzdaytext};
                \draw (-1.8cm, -.1ex) node [anchor = west, font=\footnotesize] {\pgfcalendarweekdayshortname{\pgfcalendarcurrentweekday}};
            },
            execute before day scope={
                \ifdate{day of month = 1} {
                    \pgftransformxshift{4.8cm}
                    \draw (0,0) node [minimum height = .53cm, text width = 4.4cm, fill = definedcolor, text = white, draw = definedcolor, text centered] {\textbf{\pgfcalendarmonthname{\pgfcalendarcurrentmonth}\strut}};
                }{}
                \ifdate{workday} {
                    \tikzset{every day/.style = {fill = white}}
                    \foreach \vacationBegin/\vacationEnd in \vacations {
                        \ifdate{between = \vacationBegin and \vacationEnd}{%
                            \tikzset{every day/.style = {fill = gray!30}}%
                        }{}
                    }
                    % \ifdate{between = \sommerbegin and \sommerend}{%
                    %     \tikzset{every day/.style = {fill = gray!30}}%
                    % }{}
                    % \ifdate{between = \pfingstenbegin and \pfingstenend}{%
                    %     \tikzset{every day/.style = {fill = gray!30}}%
                    % }{}
                    % \ifdate{between = \winterbegin and \winterend}{%
                    %     \tikzset{every day/.style = {fill = gray!30}}%
                    % }{}
                }{}
                \ifdate{Saturday} {
                    \tikzset{every day/.style = {fill = definedcolor!10}}%
                }{}
                \ifdate{Sunday} {
                    \tikzset{every day/.style = {fill = definedcolor!20}}%
                }{}
            },
            execute at begin day scope = {
                \pgftransformyshift{-.53*\pgfcalendarcurrentday cm}
            }
        ];
    \end{tikzpicture}

\end{document}

Variables

\newcommand*\vacations{%
    \currentyear-02-10/\currentyear-04-18,%
    \currentyear-07-28/\currentyear-10-02,%
    \currentyear-06-05/\currentyear-06-10
}

% Semester break (Winter)
% \newcommand*\winterbegin{\currentyear-02-10}
% \newcommand*\winterend{\currentyear-04-18}

% Semester break (Summer)
% \newcommand*\summerbegin{\currentyear-07-28}
% \newcommand*\summerend{\currentyear-10-02}

% Pentecost vacation
% \newcommand*\pentecostbegin{\currentyear-06-05}
% \newcommand*\pentecostend{\currentyear-06-10}

Output with Loop enter image description here

Output without Loop enter image description here


Oh and while I'm here: Is there a possibility to also write the name of the vacation inside the cell?

So that my array would look like this:

\newcommand*\vacations{%
    \currentyear-02-10/\currentyear-04-18/{Winter},%
    \currentyear-07-28/\currentyear-10-02/{Summer},%
    \currentyear-06-05/\currentyear-06-10/{Pentecost}%
}
  • 1
    Could you post a picture of the output you are getting? And it would be nice to complete your code starting from \documentclass and ending with \end{document}, making sure to include only the relevant packages so it is compilable, yet minimal. – Troy Feb 28 '17 at 16:33
  • @Troy Done that. I hope it helps :) – Sam Feb 28 '17 at 16:50
  • By loop, do you mean that you put everything inside a \foreach? In that case, \foreach is quite fragile and will crash if there are \def or \edef inside. – Symbol 1 Feb 28 '17 at 23:18
  • Yes,thats exactly what I mean. Well I don't think that it has something to do with this. After \calendar[]; I can use the same \foreach with another \newcommand "array" to input some events. \foreach \subject/\eventdate in \events {\event{\eventdate}{\subject}} where \event{}{} is defined as \newcommand*\event[2]{\node [ anchor = north west, text width = 3.4cm, font = \bfseries ] at ($(cal-#1.north west)+(3em, -0.2em)$) {\scriptsize{#2}};} – Sam Feb 28 '17 at 23:33
  • The TikZ calendar stuff ... is ... well, let's just say that, if it were me, I'd stick to the PGF layer for this. It is much easier to understand and work with. The TikZ layer is supposed to make life easier and usually does, but not for calendars/dates. There it makes things more difficult, awkward and confusing IMNSHO, obviously. YKMV. – cfr Mar 1 '17 at 0:18
8

This is a more-or-less complete reworking of the OPs code. The problem with TeX scoping of iterations (preventing the setting of the vacation keys surviving past the iteration scope) that results from using a \foreach loop is side-stepped using the .list handler from pgfkeys.

Pretty much all the styling has been moved outside the calendar code, and an additional section inside the day code checks whether the /tikz/vacation name key has been set and if so using a node (which can be customised) to typeset the vacation name. Currently the vacation name is not typeset for weekend entries, this can be changed by removing the vacation name= key from the \ifdate{Saturday} and \ifdate{Sunday} clauses.

\documentclass[landscape,a4paper,ngerman,10pt]{article}
\usepackage[utf8]{inputenc}
\usepackage[ngerman]{babel}
\usepackage[T1]{fontenc}
\usepackage{tikz,xparse}
\usepackage{lmodern}
\usepackage{translator}           
\usepackage{fancyhdr}
\usepackage[
  headheight=11ex,
  margin=.5cm,
  top=3.2cm,
  nofoot
]{geometry}
\usetikzlibrary{calc, calendar}
\renewcommand*\familydefault{\sfdefault}
\colorlet{calendar}{red}
\def\currentyear{\the\year}
\newcommand*\vacations{%
  \currentyear-02-10/\currentyear-04-18/Winter,%
  \currentyear-07-28/\currentyear-10-02/Summer,%
  \currentyear-06-05/\currentyear-06-10/Penticost
}
% Header
\renewcommand{\headrulewidth}{0pt}
\fancyhf{}
\lhead{\fontsize{60}{70}\selectfont\bfseries SS \currentyear}

\pagestyle{fancy}
\newif\ifvacation
\tikzset{%
every 1st of month/.style={
  every day/.try,
  fill=calendar, text=white, draw=calendar, text centered,
  font=\bfseries, execute at end node=\strut
},
every day/.style={
   minimum height=.53cm, 
   text width=4.4cm, 
   draw=gray,
   text=black
},
every day name/.style={
  text=black, font=\footnotesize
},
every workday/.style={
  every day/.append style={
    fill=white
  }
},
every saturday/.style={
  every day/.append style={
    fill=calendar!10
  }
},
every sunday/.style={
  every day/.append style={
    fill=calendar!20
  }
},
every vacation/.style={
  every day/.append style={
    fill=gray!30
  },
  text=black!60
},
vacation name/.initial=,
}
\begin{document}
\centering
\begin{tikzpicture}
\calendar[
  dates=\currentyear-04-01 to \currentyear-09-30,
  name=cal,
  day code={
    \node  [name=\pgfcalendarsuggestedname, every day/.try] {\tikzdaytext};
    \node  [anchor=west, every day name/.try] 
      at ([xshift=0.5cm]\pgfcalendarsuggestedname.west)
        {\pgfcalendarweekdayshortname{\pgfcalendarcurrentweekday}};
    \pgfkeysifdefined{/tikz/vacation name}{%
      \node [every vacation/.try]  {\pgfkeysvalueof{/tikz/vacation name}};
    }{}%
  },
  execute before day scope={
    \ifdate{day of month=1} {
      \pgftransformxshift{4.8cm}
      \node [every 1st of month] 
        {\pgfcalendarmonthname{\pgfcalendarcurrentmonth}};
    }{}
    \ifdate{workday} {
      \tikzset{every workday/.try, vacation name=,
        loop over item/.code args={##1/##2/##3}{%
          \ifdate{between=##1 and ##2}{%
            \tikzset{every vacation/.try, vacation name/.expanded=##3}%
          }{}},
        loop over item/.list/.expanded=\vacations}%
      }{}
    \ifdate{Saturday}{\tikzset{every saturday/.try, vacation name=}}{}%
    \ifdate{Sunday}{\tikzset{every sunday/.try, vacation name=}}{}%
   },
   execute at begin day scope={%
     \pgftransformyshift{-.53*\pgfcalendarcurrentday cm}
   }
];
\end{tikzpicture}
\end{document}

enter image description here

| improve this answer | |
  • Wow, thanks for this awesome answer! Would it also be possible to add a \node inside that loop? I want the text to appear exactly as the other text of my events. This text is placed there by this node-code: \node [ anchor = north west, text width = 3.4cm ] at ($(cal-#1.north west)+(3em, -0.2em)$) {\scriptsize{#2}} – Sam Mar 1 '17 at 15:34

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.