4

I am setting up some practice tests for an aptitude exam my son will undertake for entry to university. Many example questions are available. Hard copy books publish the questions at the front of the book and the answers at the back. Flipping pages is not conducive to understanding the question and its solution. It also makes practice under timed conditions difficult.

I'm close to setting up an e-book for practicing the test. The e-book uses Optional Content Groups (OCG) created with the hyperref and ocgx2 packages. However, I have not been able to link the choice of the correct multiple choice answer with displaying the solution. A MWE setup is as follows.

Two questions (Q1 and Q2) are set out in relation to a passage of text. Each question has five, multiple choice answers. There is a single solution for each question. The content for each passage, the questions, multiple choice answers and solution combination is loaded from an external file (DB.csv) using datatools and are set out on the page in a tcolorbox raster. The multiple choice answers are associated with radio buttons that toggle whether a particular answer choice is correct or incorrect. A separate box adjacent to the choices can be toggled to show the solution.

Currently, the solution is toggled separately from the responses to multiple choice answers. I am trying to get the solution to toggle on when the correct multiple choice answer is given. For example, the correct answer to question 1 is D. When the radio button for choice D is pressed, I want the solution to show in the adjacent box.

Thus, my question is: How do I link clicking on one ocg (a multiple choice answer) to show a separate ocg (the solution), when that link is subject to a conditional test (it is the correct answer).

This is an annotated picture of the output from the MWE enter image description here

This is the MWE:

% Adapted from https://tex.stackexchange.com/questions/286280/textfield-and-animateinline
\PassOptionsToPackage{table,x11names,dvipsnames,svgnames}{xcolor}
\documentclass[11pt,a4paper]{article}
\usepackage[utf8]{inputenc}
\usepackage[T1]{fontenc}
\usepackage{datatool}
\usepackage{enumitem}
\usepackage{fancyhdr}
\usepackage{filecontents}
\usepackage[a3paper,portrait,left=2cm,right=2cm,top=2cm,bottom=2cm,headheight=30pt]{geometry}
\usepackage{graphics} % \resizebox
\usepackage{ifthen}
\usepackage{ocgx2}
\usepackage[most]{tcolorbox}
\usepackage{xcolor}
\usepackage{hyperref} % \TextField etc.
\hypersetup{
    hyperindex=true,  
    colorlinks=true,  
    breaklinks=true,  
    urlcolor= purple,   
    linkcolor={red!50!black},
    citecolor={blue!50!black},
    urlcolor={blue!80!black},  
    linktocpage,
    bookmarks=true,  
    pdftoolbar=true,        
    pdfmenubar=true,        
    bookmarksopen=true,            
    pdftitle={LNAT},  
    pdfauthor={},    
    pdfsubject={LNAT},
}
\begin{filecontents*}{DB.csv}
    Passage,AQuestion,AChoice1,AChoice2,AChoice3,AChoice4,AChoice5,AAnswer,ASolution,BQuestion,BChoice1,BChoice2,BChoice3,BChoice4,BChoice5,BAnswer,BSolution
    "Old MacDonald had a farm. On that farm he had cows, horses, goats and sheep.",How many ruminants did McDonald have on his farm?,0,1,2,3,4,4,"Ruminants have a special stomach for fermentation of plant-based food. A horse is not a ruminant.",How many ungulates did McDonald have on his farm?,0,1,2,3,4,5,"Ungulates are hoofed animals. All McDonald's animals have hooves."
\end{filecontents*}

\newcounter{P}
\setcounter{P}{0}
\newcounter{Q}
\setcounter{Q}{0} 

\setlist{label*=\Alph*.,noitemsep,leftmargin=0.9cm}
\setlength\parindent{0pt}

\makeatletter
% patch hyperref's Form producing commands to make them layer-aware
\let\PDFFormRadioOrig\PDFForm@Radio
\def\PDFForm@Radio{\PDFFormRadioOrig\ocgxii@insert@OC}
\makeatother

%alternative check box command for layer switching    
\newcommand\layerCheckBox[3]{%
    % #1: layer name (as shown in Layers tab),
    % #2: layer id,
    % #3: initial visibility
    \raisebox{-0.6ex}{\resizebox{3ex}{!}{%
        \makebox[0pt][l]{\showocg{#2}{$\circ$}}%
        \begin{ocg}{#1}{#2}{#3}$\bullet$\end{ocg}%
    }%
    \hspace{0.1cm}
}}

% This is setting up the header
\chead{}
\cfoot{}
\fancypagestyle{myheader}{
    \fancyhead[L]{Passage \arabic{P}}
    \fancyhead[R]{\thepage{}}
    \renewcommand{\headrulewidth}{0.0pt}
    \renewcommand{\headrule}{\hbox to\headwidth{\color{blue}\leaders\hrule height \headrulewidth\hfill}}
}
\pagestyle{myheader}
% Load database 
\DTLloaddb{DB}{DB.csv}

\begin{document}
    % For each record (line) in database
    % Assign field values by name to macros
    %\DTLsetseparator{|}
    \DTLforeach{DB}{%
        \passage=Passage,
        \Aquestion=AQuestion,
        \Achoicea=AChoice1,
        \Achoiceb=AChoice2,
        \Achoicec=AChoice3,
        \Achoiced=AChoice4,
        \Achoicee=AChoice5,
        \Aanswer=AAnswer,
        \Asolution=ASolution,
        \Bquestion=BQuestion,
        \Bchoicea=BChoice1,
        \Bchoiceb=BChoice2,
        \Bchoicec=BChoice3,
        \Bchoiced=BChoice4,
        \Bchoicee=BChoice5,
        \Banswer=BAnswer,
        \Bsolution=BSolution%
    }{%
        \stepcounter{P}       

\begin{tcboxeditemize}
    [raster rows=5,raster columns=5,raster height=\textheight-2cm,arc=6pt,
    raster every box/.style={colframe=red!50!black,colback=red!10!white,coltitle=white,fonttitle=\large\bfseries}] % raster options   
    % 
    {colframe=CornflowerBlue!50!white,colback=CornflowerBlue!10!white,arc=6pt} % outer tcolorbox options
    \tcbitem[raster multicolumn=2,raster multirow=5,colframe=green!50!black,colback=white,raster height=\tcbtextheight,colbacktitle=white,coltitle=black,title=Passage \arabic{P}]
    \passage
    %
    \tcbitem[raster multicolumn=2,raster multirow=5,blankest,raster height=\tcbtextheight]
    %
    \begin{tcbitemize}[raster rows=5,raster columns=2,raster height=\tcbtextheight,colbacktitle=white]
        % Question 1
        \stepcounter{Q}
        \tcbitem[raster multicolumn=2,colframe=blue!50!white,colback=white,coltitle=black,fonttitle=\large\bfseries,title=\arabic{Q}. \Aquestion]

        \begin{enumerate}
% Question 1 - Choice A
        \item [\layerCheckBox{Q\arabic{Q}A}{\arabic{Q}A}{off} A.]  \Achoicea \par
        \begin{ocg}[radiobtngrp=Choices]{Q\arabic{Q}A}{\arabic{Q}A}{off}%
           \ifthenelse{\Aanswer=1}{Correct}{Incorrect}
        \end{ocg}%
% Question 1 - Choice B            
        \item [\layerCheckBox{Q\arabic{Q}B}{\arabic{Q}B}{off} B.] \Achoiceb \par
        \begin{ocg}[radiobtngrp=Choices]{Q\arabic{Q}B}{\arabic{Q}B}{off}%
           \ifthenelse{\Aanswer=2}{Correct}{Incorrect}
        \end{ocg}%  
% Question 1 - Choice C            
        \item [\layerCheckBox{Q\arabic{Q}C}{\arabic{Q}C}{off} C.] \Achoicec \par
        \begin{ocg}[radiobtngrp=Choices]{Q\arabic{Q}C}{\arabic{Q}C}{off}%
           \ifthenelse{\Aanswer=3}{Correct}{Incorrect}
        \end{ocg}%
% Question 1 - Choice D            
        \item [\layerCheckBox{Q\arabic{Q}D}{\arabic{Q}D}{off} D.] \Achoiced \par
        \begin{ocg}[radiobtngrp=Choices]{Q\arabic{Q}D}{\arabic{Q}D}{off}%
           \ifthenelse{\Aanswer=4}{Correct}{Incorrect}
        \end{ocg}%
% Question 1 - Choice E            
        \item [\layerCheckBox{Q\arabic{Q}E}{\arabic{Q}E}{off} E.] \Achoicee \par
        \begin{ocg}[radiobtngrp=Choices]{Q\arabic{Q}E}{\arabic{Q}E}{off}%
           \ifthenelse{\Aanswer=5}{Correct}{Incorrect}
        \end{ocg}%

        \end{enumerate}
% Question 2        
    \stepcounter{Q}
    \tcbitem[raster multicolumn=2,colframe=blue!50!white,colback=white,coltitle=black,fonttitle=\large\bfseries,title=\arabic{Q}. \Bquestion]    
        \begin{enumerate}
% Question 2 - Choice A         
        \item [\layerCheckBox{Q\arabic{Q}A}{\arabic{Q}A}{off} A.] \Bchoicea\par
        \begin{ocg}[radiobtngrp=Choices]{Q\arabic{Q}A}{\arabic{Q}A}{off}%
            \ifthenelse{\Banswer=1}{Correct}{Incorrect}
        \end{ocg}%
% Question 2 - Choice B        
        \item [\layerCheckBox{Q\arabic{Q}B}{\arabic{Q}B}{off} B.] \Bchoiceb\par
        \begin{ocg}[radiobtngrp=Choices]{Q\arabic{Q}B}{\arabic{Q}B}{off}%
            \ifthenelse{\Banswer=2}{Correct}{Incorrect}
        \end{ocg}%  
% Question 2 - Choice C        
        \item [\layerCheckBox{Q\arabic{Q}C}{\arabic{Q}C}{off} C.] \Bchoicec\par
        \begin{ocg}[radiobtngrp=Choices]{Q\arabic{Q}C}{\arabic{Q}C}{off}%
            \ifthenelse{\Banswer=3}{Correct}{Incorrect}
        \end{ocg}%
% Question 2 - Choice D        
        \item [\layerCheckBox{Q\arabic{Q}D}{\arabic{Q}D}{off} D.] \Bchoiced\par
        \begin{ocg}[radiobtngrp=Choices]{Q\arabic{Q}D}{\arabic{Q}D}{off}%
            \ifthenelse{\Banswer=4}{Correct}{Incorrect}
        \end{ocg}%
% Question 2 - Choice E        
        \item [\layerCheckBox{Q\arabic{Q}E}{\arabic{Q}E}{off} E.] \Bchoicee\par
        \begin{ocg}[radiobtngrp=Choices]{Q\arabic{Q}E}{\arabic{Q}E}{off}%
            \ifthenelse{\Banswer=5}{Correct}{Incorrect}
        \end{ocg}%
        \end{enumerate}
    \end{tcbitemize}

    \tcbitem[raster multicolumn=1,raster multirow=5,blankest,raster height=\tcbtextheight]
% These are the solutions
    \begin{tcbitemize}[raster rows=5,raster columns=1,raster height=\tcbtextheight]

% Solution to question 1        
    \tcbitem[colframe=blue!50!white,colback=white]
        \hspace{-0.6cm} \layerCheckBox{S\arabic{Q}A}{S\arabic{Q}A}{off}
        \begin{ocg}[radiobtngrp=Choices]{S\arabic{Q}A}{S\arabic{Q}A}{off}%
            \Asolution 
        \end{ocg} 

% Solution to question 2    
    \tcbitem[colframe=blue!50!white,colback=white]
    \hspace{-0.6cm} \layerCheckBox{S\arabic{Q}B}{S\arabic{Q}B}{off}
        \begin{ocg}[radiobtngrp=Choices]{S\arabic{Q}B}{S\arabic{Q}B}{off}%
            \Bsolution
        \end{ocg} 
    \end{tcbitemize}
\end{tcboxeditemize}
\clearpage
\stepcounter{P}
}
\end{document}
4
+200

In order to become visible at the same time when the correct choice button is clicked, the text in the solution box must be put on the same OCG as the corresponding choice button. In the present example, these are OCGs 1D for question One and 2E for question Two.

Note that \arabic{Q} does not expand to the correct values in the "% Solution to question ?" code sections of the original source. Therefore, the need values are hard-coded in the code below.

Moreover, individual radio button groups (e. g. radiobtngrp=Choices-1, radiobtngrp=Choices-2) should be used for different questions, because answering Q Two should not hide the choice made for Q One.

\PassOptionsToPackage{table,x11names,dvipsnames,svgnames}{xcolor}
\documentclass[11pt,a4paper]{article}
\usepackage[utf8]{inputenc}
\usepackage[T1]{fontenc}
\usepackage{datatool}
\usepackage{enumitem}
\usepackage{fancyhdr}
\usepackage{filecontents}
\usepackage[a3paper,portrait,left=2cm,right=2cm,top=2cm,bottom=2cm,headheight=30pt]{geometry}
\usepackage{graphics} % \resizebox
\usepackage{ifthen}
\usepackage{ocgx2}
\usepackage[most]{tcolorbox}
\usepackage{xcolor}
\usepackage{hyperref} % \TextField etc.
\hypersetup{
    hyperindex=true,  
    colorlinks=true,  
    breaklinks=true,  
    urlcolor= purple,   
    linkcolor={red!50!black},
    citecolor={blue!50!black},
    urlcolor={blue!80!black},  
    linktocpage,
    bookmarks=true,  
    pdftoolbar=true,        
    pdfmenubar=true,        
    bookmarksopen=true,            
    pdftitle={LNAT},  
    pdfauthor={},    
    pdfsubject={LNAT},
}
\begin{filecontents*}{DB.csv}
    Passage,AQuestion,AChoice1,AChoice2,AChoice3,AChoice4,AChoice5,AAnswer,ASolution,BQuestion,BChoice1,BChoice2,BChoice3,BChoice4,BChoice5,BAnswer,BSolution
    "Old MacDonald had a farm. On that farm he had cows, horses, goats and sheep.",How many ruminants did McDonald have on his farm?,0,1,2,3,4,4,"Ruminants have a special stomach for fermentation of plant-based food. A horse is not a ruminant.",How many ungulates did McDonald have on his farm?,0,1,2,3,4,5,"Ungulates are hoofed animals. All McDonald's animals have hooves."
\end{filecontents*}

\newcounter{P}
\setcounter{P}{0}
\newcounter{Q}
\setcounter{Q}{0} 

\setlist{label*=\Alph*.,noitemsep,leftmargin=0.9cm}
\setlength\parindent{0pt}

% hyperref Form elements not used in this example, un-comment if really needed
%\makeatletter
%% patch hyperref's Form producing commands to make them layer-aware
%\let\PDFFormRadioOrig\PDFForm@Radio
%\def\PDFForm@Radio{\PDFFormRadioOrig\ocgxii@insert@OC}
%\makeatother

%alternative check box command for layer switching    
\newcommand\layerCheckBox[3]{%
    % #1: layer name (as shown in Layers tab),
    % #2: layer id,
    % #3: initial visibility
    \raisebox{-0.6ex}{\resizebox{3ex}{!}{%
        \makebox[0pt][l]{\showocg{#2}{$\circ$}}%
        \begin{ocg}{#1}{#2}{#3}$\bullet$\end{ocg}%
    }%
    \hspace{0.1cm}
}}

% This is setting up the header
\chead{}
\cfoot{}
\fancypagestyle{myheader}{
    \fancyhead[L]{Passage \arabic{P}}
    \fancyhead[R]{\thepage{}}
    \renewcommand{\headrulewidth}{0.0pt}
    \renewcommand{\headrule}{\hbox to\headwidth{\color{blue}\leaders\hrule height \headrulewidth\hfill}}
}
\pagestyle{myheader}
% Load database 
\DTLloaddb{DB}{DB.csv}

\begin{document}
    % For each record (line) in database
    % Assign field values by name to macros
    %\DTLsetseparator{|}
    \DTLforeach{DB}{%
        \passage=Passage,
        \Aquestion=AQuestion,
        \Achoicea=AChoice1,
        \Achoiceb=AChoice2,
        \Achoicec=AChoice3,
        \Achoiced=AChoice4,
        \Achoicee=AChoice5,
        \Aanswer=AAnswer,
        \Asolution=ASolution,
        \Bquestion=BQuestion,
        \Bchoicea=BChoice1,
        \Bchoiceb=BChoice2,
        \Bchoicec=BChoice3,
        \Bchoiced=BChoice4,
        \Bchoicee=BChoice5,
        \Banswer=BAnswer,
        \Bsolution=BSolution%
    }{%
        \stepcounter{P}       

\begin{tcboxeditemize}
    [raster rows=5,raster columns=5,raster height=\textheight-2cm,arc=6pt,
    raster every box/.style={colframe=red!50!black,colback=red!10!white,coltitle=white,fonttitle=\large\bfseries}] % raster options   
    % 
    {colframe=CornflowerBlue!50!white,colback=CornflowerBlue!10!white,arc=6pt} % outer tcolorbox options
    \tcbitem[raster multicolumn=2,raster multirow=5,colframe=green!50!black,colback=white,raster height=\tcbtextheight,colbacktitle=white,coltitle=black,title=Passage \arabic{P}]
    \passage
    %
    \tcbitem[raster multicolumn=2,raster multirow=5,blankest,raster height=\tcbtextheight]
    %
    \begin{tcbitemize}[raster rows=5,raster columns=2,raster height=\tcbtextheight,colbacktitle=white]
        % Question 1
        \stepcounter{Q}
        \tcbitem[raster multicolumn=2,colframe=blue!50!white,colback=white,coltitle=black,fonttitle=\large\bfseries,title=\arabic{Q}. \Aquestion]

        \begin{enumerate}
% Question 1 - Choice A
        \item [\layerCheckBox{Q\arabic{Q}A}{\arabic{Q}A}{off} A.]  \Achoicea \par
        \begin{ocg}[radiobtngrp=Choices-1]{Q\arabic{Q}A}{\arabic{Q}A}{off}%
           \ifthenelse{\Aanswer=1}{Correct}{Incorrect}
        \end{ocg}%
% Question 1 - Choice B            
        \item [\layerCheckBox{Q\arabic{Q}B}{\arabic{Q}B}{off} B.] \Achoiceb \par
        \begin{ocg}[radiobtngrp=Choices-1]{Q\arabic{Q}B}{\arabic{Q}B}{off}%
           \ifthenelse{\Aanswer=2}{Correct}{Incorrect}
        \end{ocg}%  
% Question 1 - Choice C            
        \item [\layerCheckBox{Q\arabic{Q}C}{\arabic{Q}C}{off} C.] \Achoicec \par
        \begin{ocg}[radiobtngrp=Choices-1]{Q\arabic{Q}C}{\arabic{Q}C}{off}%
           \ifthenelse{\Aanswer=3}{Correct}{Incorrect}
        \end{ocg}%
% Question 1 - Choice D            
        \item [\layerCheckBox{Q\arabic{Q}D}{\arabic{Q}D}{off} D.] \Achoiced \par
        \begin{ocg}[radiobtngrp=Choices-1]{Q\arabic{Q}D}{\arabic{Q}D}{off}%
           \ifthenelse{\Aanswer=4}{Correct}{Incorrect}
        \end{ocg}%
% Question 1 - Choice E            
        \item [\layerCheckBox{Q\arabic{Q}E}{\arabic{Q}E}{off} E.] \Achoicee \par
        \begin{ocg}[radiobtngrp=Choices-1]{Q\arabic{Q}E}{\arabic{Q}E}{off}%
           \ifthenelse{\Aanswer=5}{Correct}{Incorrect}
        \end{ocg}%

        \end{enumerate}
% Question 2        
    \stepcounter{Q}
    \tcbitem[raster multicolumn=2,colframe=blue!50!white,colback=white,coltitle=black,fonttitle=\large\bfseries,title=\arabic{Q}. \Bquestion]    
        \begin{enumerate}
% Question 2 - Choice A         
        \item [\layerCheckBox{Q\arabic{Q}A}{\arabic{Q}A}{off} A.] \Bchoicea\par
        \begin{ocg}[radiobtngrp=Choices-2]{Q\arabic{Q}A}{\arabic{Q}A}{off}%
            \ifthenelse{\Banswer=1}{Correct}{Incorrect}
        \end{ocg}%
% Question 2 - Choice B        
        \item [\layerCheckBox{Q\arabic{Q}B}{\arabic{Q}B}{off} B.] \Bchoiceb\par
        \begin{ocg}[radiobtngrp=Choices-2]{Q\arabic{Q}B}{\arabic{Q}B}{off}%
            \ifthenelse{\Banswer=2}{Correct}{Incorrect}
        \end{ocg}%  
% Question 2 - Choice C        
        \item [\layerCheckBox{Q\arabic{Q}C}{\arabic{Q}C}{off} C.] \Bchoicec\par
        \begin{ocg}[radiobtngrp=Choices-2]{Q\arabic{Q}C}{\arabic{Q}C}{off}%
            \ifthenelse{\Banswer=3}{Correct}{Incorrect}
        \end{ocg}%
% Question 2 - Choice D        
        \item [\layerCheckBox{Q\arabic{Q}D}{\arabic{Q}D}{off} D.] \Bchoiced\par
        \begin{ocg}[radiobtngrp=Choices-2]{Q\arabic{Q}D}{\arabic{Q}D}{off}%
            \ifthenelse{\Banswer=4}{Correct}{Incorrect}
        \end{ocg}%
% Question 2 - Choice E        
        \item [\layerCheckBox{Q\arabic{Q}E}{\arabic{Q}E}{off} E.] \Bchoicee\par
        \begin{ocg}[radiobtngrp=Choices-2]{Q\arabic{Q}E}{\arabic{Q}E}{off}%
            \ifthenelse{\Banswer=5}{Correct}{Incorrect}
        \end{ocg}%
        \end{enumerate}
    \end{tcbitemize}

    \tcbitem[raster multicolumn=1,raster multirow=5,blankest,raster height=\tcbtextheight]
% These are the solutions
    \begin{tcbitemize}[raster rows=5,raster columns=1,raster height=\tcbtextheight]

% Solution to question 1        
    \tcbitem[colframe=blue!50!white,colback=white]
        \hspace{-0.6cm} \layerCheckBox{Q1D}{1D}{off}
        \begin{ocg}{Q1D}{1D}{off}%
            \Asolution 
        \end{ocg} 

% Solution to question 2    
    \tcbitem[colframe=blue!50!white,colback=white]
    \hspace{-0.6cm} \layerCheckBox{Q2E}{2E}{off}
        \begin{ocg}{Q2E}{2E}{off}%
            \Bsolution
        \end{ocg} 
    \end{tcbitemize}
\end{tcboxeditemize}
\clearpage
\stepcounter{P}
}
\end{document}
  • Thank you Alex. Now it is obvious. I need to generalize this for ~400 questions so I'll need to create unique identifiers for each OCG. That's why I used the counters to create the OCG ID's. I made a mistake when I made the MWE though. – Ross Nov 27 '17 at 10:33
  • Yes, of course, a real world questionnaire would require the use of counters to generate identifiers. I am sure you will get it right ;-). – AlexG Nov 27 '17 at 11:08

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.