Pgfkeys: <key> values und Conditionals (\ifstrequal)

Redefinition von Makros, Definition eigener Befehle sowie neuer Umgebungen


Schmantii
Forum-Anfänger
Forum-Anfänger
Beiträge: 22
Registriert: Sa 5. Dez 2015, 02:42

Pgfkeys: <key> values und Conditionals (\ifstrequal)

Beitrag von Schmantii »

Hallo zusammen!

Ich arbeite momentan ein einem kleinen Skript, dass ich mir folgende Funktionalität ermöglichen soll (vollständiges Minimalbeispiel: s. unten):
  • Ich möchte zunächst eine Umgebung (Tabelle) newlist definieren, in der ein <key> bzw. eine Option über pgfkeys eingegeben werden kann. Der <value> des keys soll im Befehl \optionstatus festgehalten werden (mögliche Werte: on / off).
    	\pgfkeys{%
    		/newlist/.is family, /newlist,
    		default/.style = {%
    			option = default},
    			option/.style = {optionlist/#1/.get = \optionstatus},
    			optionlist/.cd,
    				true/.initial = on,
    				false/.initial = off,
    				on/.initial = on,
    				default/.initial = off,
    				off/.initial = off,
    				expand/.initial = on,
    				no expand/.initial = off,
    			}
    			
    			\newenvironment{newlist}[1][]{%
    				\pgfkeys{/newlist, default, #1}%
    					\begin{longtable}{l | l}}{\end{longtable}}
    
    Die eingegebene Option soll nun in den Einträgen der Umgebung weiterverarbeitet werden. Hierbei soll für jeden Eintrag \entry ein counter erzeugt und auf 1 gesetzt werden, wenn der <key> auf on eingestellt ist (\optionstatus = on).
    			\newcommand{\entry}[1]{#1 &
    					\makeatletter
    						\ifstrequal{on}{\optionstatus}{environment option is \textbf{on}%
    							     \newcounter{#1}\setcounter{#1}{1}}%
    								{environment option is \textbf{off}}
    					\makeatother \\[0.5cm]}
    
    Im Textteil (außerhalb der Umgebung) soll dann ein Befehl ausgeführt werden, der nun wiederum den counter weiter verarbeitet. Die Standardausgabe des Befehls soll hierbei B sein.
    			\newcommand{\textcommand}{%
    					\makeatletter
    						\ifstrequal{on}{\optionstatus}{% if \optionstatus = on
    							
    							\ifstrequal{1}{\value{#1}}{%
    								command \textbf{A} will be executed \textsl{(only one time)}}% if value{command} = 1
    									{command \textbf{B} will be executed \textsl{(command A was already used)}}\stepcounter{command}}% if value{command} > 1
    								
    										{command \textbf{B} will be executed \textsl{(command option of newlist was never switched on)}}% if \optionstatus = off
    					\makeatother
    						}
    
    Ist nun

    1. die Option \optionstatus = on, so soll
    • a) wenn der counter = 1 ist, eine Alternative A ausgegeben werden bzw. ein dementsprechender Formatierungsbefehl ausgeführt werden (counter wird erhöht).

      b) wenn der counter > 1 ist, die Standardausgabe B verwendet werden.
    2. die Option \optionstatus = off, so soll die Standardausgabe B verwendet werden.
Hier das gesamte Minimalbeispiel dazu:
\documentclass{scrartcl}

\usepackage{etoolbox}
\usepackage{longtable}
\usepackage{pgfkeys}

	\pgfkeys{%
		/newlist/.is family, /newlist,
		default/.style = {%
			option = default},
			option/.style = {optionlist/#1/.get = \optionstatus},
			optionlist/.cd,
				true/.initial = on,
				false/.initial = off,
				on/.initial = on,
				default/.initial = off,
				off/.initial = off,
				expand/.initial = on,
				no expand/.initial = off,
			}
			
			\newenvironment{newlist}[1][]{%
				\pgfkeys{/newlist, default, #1}%
					\begin{longtable}{l | l}}{\end{longtable}}

			\newcommand{\entry}[1]{#1 &
					\makeatletter
						\ifstrequal{on}{\optionstatus}{environment option is \textbf{on}%
							     \newcounter{#1}\setcounter{#1}{1}}%
								{environment option is \textbf{off}}
					\makeatother \\[0.5cm]}
			
			\newcommand{\textcommand}[1]{%
					\makeatletter
						\ifstrequal{on}{\optionstatus}{% if \optionstatus = on
							
							\ifstrequal{1}{\value{#1}}{%
								command \textbf{A} will be executed \textsl{(only one time)}}% if value{#1} = 1
									{command \textbf{B} will be executed \textsl{(command A was already used)}}\stepcounter{command}}% if value{#1} > 1
								
										{command \textbf{B} will be executed \textsl{(environment option of newlist was never switched on)}}% if \optionstatus = off
					\makeatother
						}
			
\begin{document}

\noindent\textbf{table (newlist) with entries and option (switched on):}

	\begin{newlist}[option = on]
		\entry{firstentry}
		test & the true value of the environment option is \textbf{\optionstatus}\\
	\end{newlist}
	
\noindent\textbf{text with commands:}\\[0.25cm]
	
			textcommand: \textcommand{firstentry}\\[0.5cm]
						
			textcommand: \textcommand{firstentry}\\[0.25cm]

\centering	\small\textsl{$\rightarrow$ output of textcommand depends on how often the "textcommand" is used}\\[1cm]

\raggedright\noindent\textbf{what I expected:}\\[0.5cm]

			"command \textbf{A} will be executed \textsl{(only one time)}"\\[0.5cm]
		
			"command \textbf{B} will be executed \textsl{(command A was already used)}"\\[0.5cm]	
			
\end{document}
Problem ist an dieser Stelle nur, dass er scheinbar den Wert vom \optionstatus nicht richtig in \ifstrequal einbindet. Nach dem Studium der Doku zu etoolbox ist mir aufgefallen, dass \ifstrequal seine Argumente nicht expandiert - keine Ahnung, ob das möglicherweise was damit zu tun hat (funktioniert auch nicht mit \ifnumequal und einer Zahl anstatt on / off).

Der Wert "on" scheint auf jeden Fall ordnungsgemäß in die Option \optionstatus übergeben geworden zu sein (die Probe mit der Eingabe des Befehls \optionstatus gibt zumindest das Richtige aus).

Einen schönen Tag! :D

Benutzeravatar
u_fischer
Forum-Meister
Forum-Meister
Beiträge: 4267
Registriert: Do 22. Nov 2012, 11:09
Kontaktdaten:

Beitrag von u_fischer »

Ich verstehe ja nicht, warum Leute immer möglichst komplizierte Beispiel bauen. Wer soll denn da die Übersicht behalten?
\documentclass{scrartcl}

\usepackage{etoolbox}
\usepackage{pgfkeys}

   \pgfkeys{%
         option/.style = {optionlist/#1/.get = \optionstatus},
         optionlist/.cd,
            on/.initial = on,
            off/.initial = off,
         }

    

\begin{document}

\pgfkeys{option=on}
\ifdefstring{\optionstatus}{on}{on}{off}%
                  
\pgfkeys{option=off}
\ifdefstring{\optionstatus}{on}{on}{off}%

\end{document} 
N.B: Deine \makeatletter/\makeatother sind unsinnig, die tun da gar nichts.

Schmantii
Forum-Anfänger
Forum-Anfänger
Beiträge: 22
Registriert: Sa 5. Dez 2015, 02:42

Beitrag von Schmantii »

Hallo Frau Fischer!

Tut mir leid, wenn das zu detailreich gewesen ist. Ich habe das Beispiel dementsprechend gekürzt und ihren Vorschlag implementiert.
\documentclass{scrartcl} 

\usepackage{pgfkeys}
\usepackage{etoolbox}

   \pgfkeys{% 
      /newlist/.is family, /newlist, 
      default/.style = {option = off}, 
         option/.style = {optionlist/#1/.get = \optionstatus}, 
         optionlist/.cd, 
            on/.initial = on, 
            default/.initial = off, 
            off/.initial = off}
          
         \newenvironment{newlist}[1][]{% 
            \pgfkeys{/newlist,  #1}% 
               \begin{tabular}{ll}}{\end{tabular}\vspace*{3cm}}

         \newcommand{\entry}[1]{	& %
         		\ifdefstring{\optionstatus}{on}{%
         					on \newcounter{#1}\setcounter{#1}{1}}{ %
         					off}\\ %
         							}
          
         \newcommand{\textcommand}[1]{% 
                  \ifdefstring{\optionstatus}{on}{%
                     \ifnum\value{#1}=1%
                     		A \stepcounter{#1}%
                     \else %
                     		B %
                     \fi}{C} %
                     }
          
\begin{document} 

   \begin{newlist}[option =on]
      \entry{entry}
      Test: & \optionstatus\\ 
   \end{newlist} 
   
\noindent   		Zu erwarten ist "A":  \textcommand{entry}\\[0.5cm] 
\noindent   		Zu erwarten ist "B":   \textcommand{entry}\\[0.25cm] 
          
\end{document} 
Die Übergabe des Befehls \optionstatus zwischen der Umgebung list und dem Listeneintrag \entry scheint zu funktionieren. Allerdings scheint dies außerhalb der Umgebung nicht der Fall zu sein. Ich erhalte hier weiterhin das falsche Ergebnis (CC anstatt von AB).

Besserwisser

Beitrag von Besserwisser »

Ich bin zwar nicht Frau Fischer, aber es ist korrekt, dass Definitionen lokal zur jeweiligen Umgebung sind. Wenn es global wirken soll, musst du das explizit veranlassen:
\documentclass{scrartcl}

\usepackage{pgfkeys}
\usepackage{etoolbox}

   \pgfkeys{%
      /newlist/.is family, /newlist,
      default/.style = {option = off},
         option/.style = {optionlist/#1/.get = \optionstatus},
         optionlist/.cd,
            on/.initial = on,
            default/.initial = off,
            off/.initial = off}
         
         \newenvironment{newlist}[1][]{%
            \pgfkeys{/newlist,  #1}%
            \global\let\optionstatus\optionstatus
               \begin{tabular}{ll}}{\end{tabular}\vspace*{3cm}}

         \newcommand{\entry}[1]{   & %
               \ifdefstring{\optionstatus}{on}{%
                        on \newcounter{#1}\setcounter{#1}{1}}{ %
                        off}\\ %
                              }
         
         \newcommand{\textcommand}[1]{%
                  \ifdefstring{\optionstatus}{on}{%
                     \ifnum\value{#1}=1%
                           A \stepcounter{#1}%
                     \else %
                           B %
                     \fi}{C} %
                     }
         
\begin{document}

   \begin{newlist}[option =on]
      \entry{entry}
      Test: & \optionstatus\\
   \end{newlist}
   
\noindent         Zu erwarten ist "A":  \textcommand{entry}\\[0.5cm]
\noindent         Zu erwarten ist "B":   \textcommand{entry}\\[0.25cm]
         
\end{document}
Das hat dann aber auch Auswirkungen, wenn newlist-Umgebungen geschachtelt werden.

Da du eine KOMA-Script-Klasse verwendest, sei darauf hingewiesen, dass KOMA-Script sein eigenes key-value-System bereitstellt:
\documentclass{scrartcl}

\DefineFamily{environments}
\DefineFamilyMember[.newlist]{environments}
\FamilyBoolKey[.newlist]{environments}{option}{newlistoption}
% Wenn der Schalter für die Option global wirken soll:
\renewcommand*{\newlistoptionfalse}{\global\let\ifnewlistoption\iffalse}
\renewcommand*{\newlistoptiontrue}{\global\let\ifnewlistoption\iftrue}

\newenvironment{newlist}[1][]{%
  \FamilyExecuteOptions[.newlist]{environments}{#1}%
  \begin{tabular}{ll}%
}{%
  \end{tabular}\vspace*{3cm}
}
\newcommand{\entry}[1]{   & %
  \ifnewlistoption on \newcounter{#1}\setcounter{#1}{1}%
  \else off \fi
  \\%
}
\newcommand\optionstatus{\ifnewlistoption on\else off\fi}
        
\newcommand{\textcommand}[1]{%
  \ifnewlistoption
    \ifnum\value{#1}=1
      A \stepcounter{#1}%
    \else
      B %
    \fi
  \else C\fi
}
         
\begin{document}

   \begin{newlist}[option=on]
      \entry{entry}
      Test: & \optionstatus\\
   \end{newlist}
   
\noindent         Zu erwarten ist "A":  \textcommand{entry}\\[0.5cm]
\noindent         Zu erwarten ist "B":   \textcommand{entry}\\[0.25cm]
         
\end{document}
Näheres dazu ist im Kapitel zu scrbase der KOMA-Script-Anleitung zu finden.

Schmantii
Forum-Anfänger
Forum-Anfänger
Beiträge: 22
Registriert: Sa 5. Dez 2015, 02:42

Beitrag von Schmantii »

Besserwisser hat geschrieben:Ich bin zwar nicht Frau Fischer (...)
Kein Ding - ich bin für jede Hilfe in höchstem Maße dankbar. :D
Besserwisser hat geschrieben: Das hat dann aber auch Auswirkungen, wenn newlist-Umgebungen geschachtelt werden.
Verstehe ich das mit Schachtelung richtig, dass du so etwas wie
   \begin{newlist}
       \begin{newlist}
 
       \end{newlist}
    \end{newlist}
meinst?
Wenn ja, dann ist das (zumindest für mich) zunächst erstmal kein Problem - mein Anwendungshintergrund sieht nur die einfache Verwendung der Umgebung vor, so dass hier kein Problem entstehen sollte.
Besserwisser hat geschrieben: Da du eine KOMA-Script-Klasse verwendest, sei darauf hingewiesen, dass KOMA-Script sein eigenes key-value-System bereitstellt (...)
Und das ist auf jeden Fall auch gut zu wissen - werde ich mir bei Gelegenheit und Notwendigkeit noch genauer zu Gemüte führen (bislang reicht pgfkeys noch vollkommen für meine Zwecke aus)! ;)

Nach ein bisschen Rumprobieren habe ich nun deinen Vorschlag in meine Anwendung eingebaut. Funktioniert; auch wenn ich danach bestimmt noch 2 Stunden damit verbracht habe, einen echt unnötigen Fehler zu finden...


Man vertausche bitte nie in
\ifdefstring{<\command>}{<string>}{<if>}{<else>}
das <\command> und <string>! Das führt zu Frust und Zeitverschwendung bei der Fehlersuche sowie dem Gefühl, dass man sich gerade selbst (unbewusst) verarscht hat. :?



Trotzdem habe ich noch eine Fragestellung, die bislang (trotz Lösung) offen geblieben ist. Bei der Programmierung eines Tabelleneintrags (mit Optionen)
    \pgfkeys{% 
    	/entry/.is family, /entry, 
    	default/.style = {left= 1cm,
					    		right = 5cm},
			left/.estore in = \leftindent,
			right/.estore in = \rightindent} 
    
          \newcommand{\entry}[3][]{%
          	\pgfkeys{/entry/.cd, default, #1}
          	\hspace*{\leftindent} #2 & %
                \hspace*{\rightindent} #3\\} 
ist mir aufgefallen, dass das Programm immer die Fehlermeldung !Undefined control sequence. <argument> \rightindent ausgibt, was sich allerdings durch das Einfügen eines weiteren \pgfkeys{/entry/.cd, default, #1} in der rechten Spalte / Zelle beheben lässt. Gibt es da ggf. eine elegantere Lösung das \pgfkeys{...} auch global auf alle Zellen anzuwenden? Bei größeren Quellcodes oder mehr Spalten kann das ziemlich schnell unübersichtlich und nervig werden...

Trotzdem schon mal vielen Dank für die bisherige Hilfe! Hat mich wiedermal ein ganz großes Stück weitergebracht. :D

Benutzeravatar
u_fischer
Forum-Meister
Forum-Meister
Beiträge: 4267
Registriert: Do 22. Nov 2012, 11:09
Kontaktdaten:

Beitrag von u_fischer »

Schmantii hat geschrieben: (bislang reicht pgfkeys noch vollkommen für meine Zwecke aus)! ;)
Das ist die falsche Formulierung. pgfkeys ist eines der mächtigsten keyval-Systeme. Ein ähnliches System bietet das neue options Paket.

Ich persönlich habe es lieber, wenn es einen key-define- und einen key-set-Befehl gibt, daher ziehe ich, wenn ich die Wahl habe, l3keys vor. Das System von KOMA ist mir zu speziell.

Schmantii
Forum-Anfänger
Forum-Anfänger
Beiträge: 22
Registriert: Sa 5. Dez 2015, 02:42

Neues Problem (Abfolge der Befehle mit Pgfkeys)

Beitrag von Schmantii »

u_fischer hat geschrieben: Das ist die falsche Formulierung. pgfkeys ist eines der mächtigsten keyval-Systeme.
Das habe ich mittlerweile auch gemerkt; funktioniert auch echt super! :)

Allerdings habe ich jetzt ein neues Problem, für das ich mittlerweile auch die Ursache gefunden habe - daher mache ich das Thema jetzt noch einmal neu auf.
\documentclass{scrartcl}


\usepackage{pgfkeys}
	\usepackage{etoolbox}
	
	\pgfkeys{/Test/.is family, /Test/.cd,Testkey/.code = \csgdef{Testkey}{#1}}
	
\newenvironment{Testumgebung}[1][]{\pgfkeys{Test/.cd,#1}}{}

\newcommand{\Befehl}{
	\ifcsdef{Testkey}{%
		\ifcsstring{Testkey}{on}{Key ist definiert.}{Key ist \textsl{nicht} definiert.}}{Key existiert nicht.}}

\begin{document}
	
	\Befehl
	
	\begin{Testumgebung}[Testkey=on]
		
	\end{Testumgebung}

	\Befehl

\end{document}
Ich möchte eine Umgebung definieren (oben als Testumgebung beschrieben), die einen globalen Key (Testkey) definiert. Dieser Key bzw. seine Existenz wird dann in einem Befehl (Befehl) ausgewertet, was zu einer dementsprechenden Ausgabe führt.

Jetzt habe ich aber nach langer Fehlersuche festgestellt, dass die Position des Befehls (wie auch logisch anzunehmen...) entscheidend dafür ist, ob der Befehl richtig ausgeführt wird oder nicht (beabsichtigtes Verhalten beider Befehle: Key ist definiert), da die Befehle hintereinander abgespult werden und je nach Position der Umgebung Testumgebung der Key Testkey initial definiert wird. Ich möchte aber ein positionsunabhängiges Verhalten des Befehls erreichen - wie kann ich das erreichen? Ich bin zwar mittlerweile recht fit in LaTeX, aber dafür reicht's dann leider doch noch nicht...

Schönen Gruß und danke nochmal wegen der ganzen Rückmeldungen! :)

Besserwisser

Beitrag von Besserwisser »

Das geht nur, wenn du die Schlüsseleinstellungen in die aux-Datei schreibst. Allerdings halte ich den Ansatz grundsätzlich für fragwürdig. Was soll denn passieren, wenn der Schlüssel mehrfach gesetzt wird?

Beachte bitte, dass der Status Rückfrage nicht für Folgefragen gedacht ist.

Schmantii
Forum-Anfänger
Forum-Anfänger
Beiträge: 22
Registriert: Sa 5. Dez 2015, 02:42

Beitrag von Schmantii »

Besserwisser hat geschrieben:Was soll denn passieren, wenn der Schlüssel mehrfach gesetzt wird?
Naja, es geht hier eher weniger darum, dass der Schlüssel Testkey mehrfach gesetzt wird, sondern dass der \Befehl sich - abhängig vom im Schlüssel enthaltenen Text (on / off) - an die Umgebung anpassen soll (über
\ifcsdef{Testkey}{...}{...}
im Befehl). Die Umgebung könnte zwar auch öfters gesetzt werden, allerdings geht es mir hier erstmal nur um die erfolgreiche Übergabe des Testkey-Wertes an den \Befehl (ich vermute mal, dass eine Mehrfachsetzung der Umgebung in meiner Anwendung dann auch funktionieren sollte...).

Ich habe jetzt einfach mal u. a. versucht, mit
	\pgfkeys{/Test/.is family, /Test/.cd,Testkey/.code = \write\@auxout{\csgdef{Testkey}{#1}}}
und folgendem Beitrag (http://texwelt.de/wissen/fragen/8072/wi ... ter-setzen) zu arbeiten - fruchtet aber nicht wirklich.
Besserwisser hat geschrieben:Beachte bitte, dass der Status Rückfrage nicht für Folgefragen gedacht ist.
Sorry dafür - hatte ich nicht bedacht. Habe den Status erstmal wieder auf offen (?) gesetzt.

Besserwisser

Beitrag von Besserwisser »

Ich würde dir empfehlen: Wenn der Schalter für das ganze Dokument gelten soll und nicht nur für die Umgebung, in der er verwendet wird, dann mach aus dem Ganzen ein Paket und definiere einen Befehl, über den man in der Präambel bereits die Schalter setzt. Dass ein Schalter, der mitten im Dokument gesetzt wird auch bereits an Anfang des Dokuments gelten soll, ist uneinsichtig und für den Anwender nicht nachvollziehbar.

Wenn ein Schalter ausnahmsweise trotzdem über das Dokumentende hinaus für den nächsten LaTeX-Lauf Auswirkungen haben soll, dann schreib am Ende des Dokument (siehe \AtEndDocument oder mit scrlfile auch \BeforeClosingMainAux) entsprechende Infos in die aux-Datei. Wichtig zu wissen dabei ist: Die aux-Datei wird zweimal gelesen, einmal innerhalb von \begin{document} und einmal unmittelbar nachdem sie geschlossen wurde innerhalb von \end{document}.

Antworten