LaTeX-Beamer-Frame dynamisch basierend auf Tags ausblenden

LaTeX-Beamer-Frame dynamisch basierend auf Tags ausblenden

Ich glaube, ich bin in einer Situation, die vielen bekannt vorkommt: Im Rahmen eines mehrjährigen Projekts habe ich eine sehr große Beamer-Präsentation mit vielen Folien entwickelt. Die LaTeX-Quellen sind auf viele Dateien aufgeteilt und da ich die Präsentation des Projekts oft neu erstellen muss, füge ich diese Dateien je nach Zeit/Publikum in das Hauptdokument ein (oder auch nicht).

Nun möchte ich die Generierung der Präsentation irgendwie automatisieren, indem ich den Rahmen steuere, den ich einbinden möchte. Statisch scheint dies mit den <handout:0|beamer:0>Parametern bei der Rahmendefinition möglich zu sein:

\begin{frame}
Hello.
\end{frame}

% this frame won't be included
\begin{frame}<handout:0|beamer:0>
  Hidden
\end{frame}

Ich möchte den Ansatz allgemeiner gestalten. Daher dachte ich über die Möglichkeit nach,

  • [einen Satz von] Tags bei der Frame-Definition zuordnen
  • Entscheiden Sie später (auf irgendeine Weise) über den zu verwendenden Tag-Satz beim Kompilieren der Präsentation, sodass nur die Frames mit diesem Tag berücksichtigt werden (und die anderen somit ausgeblendet werden).

Kennt jemand eine Möglichkeit, das zu tun?

Antwort1

Das ist eine wirklich schöne Frage!

Dies ist der Beginn einer Lösung, die stark inspiriert ist vonKommentar von Wernerund mit wenigen Einschränkungen. Diese sind auf die Tatsache zurückzuführen, dass die Lösung auf Beamer basiert. Aus diesem Grund kennzeichne ich es als Community-Wiki und jeder kann es verbessern (und es gibt viel Raum dafür).

Es bietet eine einfache Benutzeroberfläche mit dem Ziel,

  • Erstellen von Tags zur Unterscheidung der Version(en);
  • Entscheiden, welche Version(en) ausgegeben werden.

Die erste Aufgabe übernehmen die Tasten

  1. create new version
  2. create multiple versions

und die Namen sind ziemlich klar. Um zu entscheiden, welche Frames ausgegeben werden, nutzen Sie:

  1. compile version
  2. compile framesum eine Teilmenge von Frames innerhalb einer Version auszuwählen
  3. compile generic framesum Frames aus verschiedenen Versionen auszuwählen.

Wie gesagt, der Mechanismus ist in Werners Kommentar ziemlich genau beschrieben: Das Erstellen einer neuen Version bedeutet einfach, dass ein neuer Zähler (oder mehrere) instanziiert und jedes Mal aktualisiert wird, wenn ein Frame auf diese Weise markiert wird. Das Kompilieren von Versionen wird im Hintergrund durch die Erstellung einer Liste durchgeführt, die an übergeben wird \includeonlyframes. So weit, so gut.

Sehen wir uns nun die Grenzen und Schwachstellen des Ansatzes an. Frames auf die oben beschriebene Weise zu taggen, ohne wirklich unter Beamer zu gehen, erfordert einen kleinen Trick, nämlich eine neue Umgebung. Zu diesem Zweck sebframewurde eine Umgebung definiert, die ein optionales Argument mit der folgenden Syntax akzeptiert

<tag>-<list of beamer options>

Das Zeichen -dient dazu, den Tag von anderen Beamer-Optionen abzugrenzen; hier geht es übrigens mit daneben fragile.

Das mwe. Es ist kommentiert und wird als Proof of Concept betrachtet: Ein paar Dinge können verbessert werden, wie die Kommentare zeigen.

\documentclass{beamer}
\usepackage{lmodern} % https://tex.stackexchange.com/a/58088/13304
\usepackage{mwe} % for dummy image

\newcommand{\enlarge}{\centering\scalebox{13}}% Just for this example

% = = = = = = = = = = = = = = = = = = = = = = = =
% core of the solution

\usepackage{pgffor,pgfkeys,xstring}

\newcommand{\deflistframes}[1]{
\gdef\listframes{}% global list
\foreach \x[count=\xi] in {#1}{\global\let\maxitems\xi}% count the max number of items
\foreach \x[count=\xi] in {#1}{
 \ifnum\xi=\maxitems
   \xdef\listframes{\listframes \vers\x}
 \else
   \xdef\listframes{\listframes \vers\x,}
 \fi
}
}

\newcommand{\defcustomlistframes}[1]{
\gdef\listframes{}% global list
\foreach \x[count=\xi] in {#1}{\global\let\maxitems\xi}% count the max number of items
\foreach \x[count=\xi] in {#1}{
 \ifnum\xi=\maxitems
   \xdef\listframes{\listframes \x}
 \else
   \xdef\listframes{\listframes \x,}
 \fi
}
}

\makeatletter
% https://tex.stackexchange.com/a/74890/13304
\newcommand{\defversion}[1]{\@namedef{vers#1}{}}
\makeatother

% test for multiple versions compilation
\newif\ifmultipleversion
\multipleversionfalse


\pgfkeys{/seb-beamer/.cd,
  create new version/.code={\gdef\vers{#1}\newcounter{#1}},
  create multiple versions/.code={%
    \gdef\listofversions{}
    \foreach \v in {#1}{%
      \defversion{\v}
      \newcounter{\v}
      \xdef\listofversions{\listofversions \v,}
    }%
    \multipleversiontrue
  },
  compile version/.code={% version-based: need to store in .aux a
    % "total frame" counter per version (incremented in custom enviroment)
    % and use it as final value (hard-coded here for simplicity)
    \deflistframes{1,...,1000}
    % list of frames in output
    \expandafter\includeonlyframes\expandafter{\listframes}%
  }, 
  compile frames/.code={% this should be version-based -> second argument (TBA)
    \deflistframes{#1}
    % for the list
    \expandafter\includeonlyframes\expandafter{\listframes}%
  }, 
  compile generic frames/.code={% generic for multi version compilations
   \defcustomlistframes{#1}
   % list of frames in output
   \expandafter\includeonlyframes\expandafter{\listframes}%
  }, 
}

\makeatletter

% see: https://tex.stackexchange.com/a/132411/13304
\newenvironment{sebframe}[1][]{
% cut the string to separate version from other options
\StrCut{#1}{-}\compvers\opt
\edef\seboptions{\opt}
\def\mylabel{}
\ifmultipleversion
  \@ifundefined{c@\compvers}{}{% test if the counter is undefined
    \stepcounter{\compvers}
    % increment here the counter total frames per version
    \edef\mylabel{label=\compvers\the\value{\compvers}}
    }
\else
 \IfStrEq{\vers}{\compvers}{% true
  \stepcounter{\compvers}
  % increment here the counter total frames per version
  \edef\mylabel{label=\compvers\the\value{\compvers}}
 }{% false
  \edef\mylabel{}
 }
\fi
\begingroup\edef\x{\endgroup\noexpand\begin{frame}[\mylabel,\seboptions]}\x}{\end{frame}}

\makeatother

\newcommand{\SebCompileOptions}[1]{%
 \pgfkeys{/seb-beamer/.cd,#1}
}

% = = = = = = = = = = = = = = = = = = = = = = = =
% user interface

%\SebCompileOptions{create new version=A, compile version=A}
%\SebCompileOptions{%
% create multiple versions={A,B}, 
% compile generic frames={A1,A3,B1,B2,C3}
%}
\SebCompileOptions{create new version=C, compile frames={1,3}}


\begin{document}

\begin{sebframe}[A-t,allowframebreaks]
  \frametitle{Version A}
  \enlarge{A1}
\end{sebframe}

\begin{sebframe}[B]
  \frametitle{Version B}
  \enlarge{B1}
\end{sebframe}

\begin{sebframe}[C]
  \frametitle{Version C}
  \enlarge{C1}
\end{sebframe}

\begin{sebframe}[A]
  \frametitle{Version A}
  \enlarge{A2}
\end{sebframe}

\begin{sebframe}[B]
  \frametitle{Version B}
  \enlarge{B2}
\end{sebframe}

\begin{sebframe}[C]
  \frametitle{Version C}
  \enlarge{C2}
\end{sebframe}

\begin{frame}
  \frametitle{Version B or C}
  \enlarge{BC}
\end{frame}

\begin{sebframe}[A-t,b,containsverbatim]% just to point out that passing multiple beamer options works (unless fragile)
  \frametitle{Version A}
  \enlarge{A3}
\end{sebframe}

\begin{sebframe}[C]
  \frametitle{Version C}
  \enlarge{C3}
  \vskip1ex
  \includegraphics<2>[scale=0.5]{example-image}
\end{sebframe}

\begin{sebframe}[A]
  \frametitle{Version A}
  \enlarge{A4}
\end{sebframe}

\begin{frame}[fragile,label=A5]% add manually a label: displayed only in A version though
  \frametitle{Version A \& B \& C}
  \enlarge{ABC}
\end{frame}


\end{document}

Wie wir ausgewählt haben:

\SebCompileOptions{create new version=A, compile version=A}

die Ausgabe ist:

Bildbeschreibung hier eingeben

Durch die Auswahl

\SebCompileOptions{create new version=C, compile frames={1,3}}

Bildbeschreibung hier eingeben

Beachten Sie, dass Overlay-fähige Befehle weiterhin funktionieren und Frame C2 nicht angezeigt wird. Versuchen wir

\SebCompileOptions{%
 create multiple versions={A,B}, 
 compile generic frames={A1,A3,B1,B2,C3}
}

was ergibt:

Bildbeschreibung hier eingeben

Da Version C nicht definiert ist, wird der Rahmen C3 erwartungsgemäß nicht angezeigt. Die Reihenfolge, in der die Liste bereitgestellt wird, hat keinen Einfluss auf die Reihenfolge des Erscheinens im PDF: Dafür zählen nur die Seitenzahlen.

Antwort2

Ich würde eine Reihe von Konditionalen erstellen, eine für jede Art von Präsentation, die Sie halten möchten. Nennen wir diese Typen „Versionen“. Vielleicht gibt es also VersionA, VersionBund VersionC.

Erstellen Sie für jede Version eine Bedingung mit \newif(standardmäßig ist sie auf „false“ gesetzt):

\newif\VersionA
\newif\VersionB
\newif\VersionC

Dann packen Sie Ihre Folien, die mit einer bestimmten Version verknüpft sind. Eine Folie würde framebeispielsweise aussehenVersionA

\ifVersionA
\begin{frame}
  ...
\end{frame}
\fi

VersionBWenn Sie eine Folie haben, die mit (sagen wir) OR verknüpft werden soll VersionC, dann können Sie Folgendes tun:

\makeatletter
\let\ifCombined\ifVersionB
\ifVersionC
  \expandafter\@firstoftwo
\else
  \expandafter\@secondoftwo
\fi
  {\let\ifCombined\iftrue}{}
\makeatother

\ifCombined% Version B or C
\begin{frame}
  ..
\end{frame}
\fi

Die Folien, die in allen Versionen sichtbar sind, benötigen keine bedingte Umbruchfunktion. Hier ist ein minimales Beispiel für die Einrichtung:

Bildbeschreibung hier eingeben

\documentclass{beamer}% http://ctan.org/pkg/beamer
\let\Tiny=\tiny% http://tex.stackexchange.com/q/58087/5764
\newcommand{\enlarge}{\centering\scalebox{13}}% Just for this example

\newif\ifVersionA
\newif\ifVersionB
\newif\ifVersionC


%\VersionAtrue% Show Version A
\VersionBtrue% Show Version B
%\VersionCtrue% Show Version C

\begin{document}

\ifVersionA
\begin{frame}
  \frametitle{Version A}
  \enlarge{A1}
\end{frame}
\fi

\ifVersionB
\begin{frame}
  \frametitle{Version B}
  \enlarge{B1}
\end{frame}
\fi

\ifVersionC
\begin{frame}
  \frametitle{Version C}
  \enlarge{C1}
\end{frame}
\fi

\ifVersionA
\begin{frame}
  \frametitle{Version A}
  \enlarge{A2}
\end{frame}
\fi

\ifVersionB
\begin{frame}
  \frametitle{Version B}
  \enlarge{B2}
\end{frame}
\fi

\makeatletter
\let\ifCombined\ifVersionB
\ifVersionC
  \expandafter\@firstoftwo
\else
  \expandafter\@secondoftwo
\fi
  {\let\ifCombined\iftrue}{}
\makeatother

\ifCombined% Version B or C
\begin{frame}
  \frametitle{Version B or C}
  \enlarge{BC}
\end{frame}
\fi

\ifVersionA
\begin{frame}
  \frametitle{Version A}
  \enlarge{A3}
\end{frame}
\fi

\ifVersionC
\begin{frame}
  \frametitle{Version C}
  \enlarge{C3}
\end{frame}
\fi

\ifVersionA
\begin{frame}
  \frametitle{Version A}
  \enlarge{A4}
\end{frame}
\fi

\begin{frame}
  \frametitle{Version A \& B \& C}
  \enlarge{ABC}
\end{frame}

\end{document}

Ähnliches können Sie mit demcommentPaket. Allerdings ist das Kombinieren von Bedingungen nicht so einfach. Eine Problemumgehung wäre natürlich, Folien mit Informationen für mehrere Versionen zu duplizieren.

Eine weitere praktikable Option wäre die Erstellung eines labelfür jedes framemithilfe von

\begin{frame}[label=ConceptX]
  ...
\end{frame}

oder mit dem \frameBefehl. Und fügen Sie dann ConceptXdie Versionen hinzu, die Sie interessieren, einschließlich der Verwendung

\includeonlyframes{...,ConceptX,...}% Version A

frameDies erfordert allerdings, dass Sie jedem eine konzeptionelle (eindeutige) Bezeichnung geben .

verwandte Informationen