Anzeige:
Ergebnis 1 bis 7 von 7

Thema: Key-Value-Liste aus Box/Makros erzeugen

  1. #1
    Registrierter Benutzer
    Registriert seit
    26.02.2010
    Beiträge
    7

    Question Key-Value-Liste aus Box/Makros erzeugen

    Hallo zusammen.

    das dies mein erstes Posting in diesem Forum ist, möchte ich
    die Gelegenheit kurz nutzen, um mich für all die interessanten
    Fragen und Antworten zu bedanken, die ich als Gast gelesen
    und zur Lösung meiner LaTeX-Probleme genutzt habe. Danke!

    Zur nachfolgenden Frage habe ich in den Archiven leider
    keine Antwort gefunden. Ich hoffe, ich habe auch gründlich
    genug gesucht ...

    Ich möchte gerne eine Key-Value-Liste erzeugen (wie in keyval.sty
    beschrieben) und diese für Befehle wie \includegraphics[...]{...} mehrfach
    nutzen. Hierzu wollte ich entweder eine Box (z.B. \newsavebox} oder
    ein Makro verwenden. Leider gelingt es mir nicht, weil der parser von
    keyval.sty den Inhalt meiner Box oder meines Makros nicht splitten kann.

    Hier ein kleines Beispiel:

    Code:
    \documentclass{article}
    
    \usepackage{keyval}
    
    % command
    \newcommand{\tmpA}{}
    \newcommand{\tmpB}{}
    \newcommand{\tmpC}{}
    \newcommand{\tmpD}{}
    \newcommand{\myscale}{}
    \newcommand{\mysetkeys}[1][]{\renewcommand{\myscale}{undef!}\setkeys{TEST}{#1}}
    
    % key
    \makeatletter 
    \define@key{TEST}{scale}[1]{\renewcommand{\myscale}{#1}}
    \makeatother  
    
    \begin{document}
    % init
    \renewcommand{\tmpA}{0.3}
    \renewcommand{\tmpB}{=0.3}
    \renewcommand{\tmpC}{scale=0.3}
    
    % test 1
    \mysetkeys[scale=\tmpA]
    output: \myscale \\	% funktioniert
    
    % test 2
    \mysetkeys[scale\tmpB]
    output: \myscale \\	% funktioniert nicht!
    
    % test 3
    \mysetkeys[\tmpC]
    output: \myscale \\	% funktioniert nicht!
    
    \end{document}
    Die Fehlermeldung für test 2 und test 3 lautet:
    ! Package keyval Error: scale=0.3 undefined.

    Das splitting in keyval.sty erfolgt mit \KV@split#1=#2=#3\relax{...}.
    Ich kann leider nicht behaupten, dass ich die TeX-Anweisungen
    verstehe. Beiden Fehlerfällen ist jedoch gemein, dass anscheinend das
    Gleichheitszeichen zur Trennung von und nicht
    gefunden wird.

    Ist das was ich vorhabe generell möglich?

    Vielen Dank und beste Grüße, Mark.

  2. #2
    Registrierter Benutzer
    Registriert seit
    19.05.2009
    Beiträge
    4.045
    Mit einer Box geht es gar nicht. Schlüssellisten in einem Befehl zu speichern, geht im Prinzip, aber da keyval+Konsorten die Liste lexikalisch scannen, muss der Befehl vorher expandiert werden.

    Eine Möglichkeit ist z.B.

    Code:
    \newcommand{\mysetkeys}[1][]{%
     \renewcommand{\myscale}{undef!}%
      \edef\next{\noexpand\setkeys{TEST}{#1}}%
      \next}
    Das expandiert aber #1 komplett, was manchmal zuviel sein kann. Eine andere Möglichkeit ist ein toks-Register zu benutzen. Den Unterschied zwischen den Varianten kannst du sehen, wenn du die Ausgabe von \show\next miteinander vergleichst.

    Code:
    \documentclass{article}
    
    \usepackage{keyval}
    
    % command
    \newcommand{\tmpA}{}
    \newcommand{\tmpB}{}
    \newcommand{\tmpC}{}
    \newcommand{\tmpD}{}
    \newcommand{\myscale}{}
    \makeatletter
    \newcommand{\mysetkeysToks}[1][]{%
     \renewcommand{\myscale}{undef!}%
      \toks@=\expandafter{#1}%
      \edef\next{\noexpand\setkeys{TEST}{\the\toks@}}%
      \show\next
      \next}
    \makeatother
    
    \newcommand{\mysetkeys}[1][]{%
     \renewcommand{\myscale}{undef!}%
      \edef\next{\noexpand\setkeys{TEST}{#1}}%
      \show\next
      \next}
    
    % key
    \makeatletter
    \define@key{TEST}{scale}[1]{\renewcommand{\myscale}{#1}}
    \makeatother
    
    \begin{document}
    % init
    \renewcommand{\tmpA}{0.3}
    
    \renewcommand{\tmpC}{scale=0.3}
    \mysetkeys[\tmpC]
    \mysetkeysToks[\tmpC]
    output: \myscale \\	
    
    \renewcommand{\tmpC}{scale=\tmpA}
    \mysetkeys[\tmpC]
    \mysetkeysToks[\tmpC]
    output: \myscale \\	
    \end{document}

  3. #3
    Registrierter Benutzer
    Registriert seit
    26.02.2010
    Beiträge
    7

    Question

    Hallo Ulrike,
    vielen Dank für Deine Hilfe. Ich bin schon mal sehr froh, dass es überhaupt klappt. Leider habe ich die Lösung noch nicht verstanden.

    Code:
    \makeatletter   
    \newcommand{\mysetkeysToks}[1][]{%
     \renewcommand{\myscale}{undef!}%
      \toks@=\expandafter{#1}%
      \edef\next{\noexpand\setkeys{TEST}{\the\toks@}}%
      \show\next
      \next}
    \makeatother
    Was machen die rot gesetzten Komanndos? Ich habe mir die Kommandos in "Einführung in TEX" von N. Schwarz angeschaut und habe eine vage Idee mit vielen Lücken ...

    \toks@=\expandafter{#1}

    Kopiert und parst den Parameter #1 in das token register 0 (\toks@).
    Worauf zielt das \expandafter ab? Wird zunächst die nachfolgende Makrodefinition:

    \edef\next{\noexpand\setkeys{TEST}{\the\toks@}}

    betrachtet? Warum muss ich hier \edef nehmen wenn ich die Expansion mit \noexand verhindere? Welche Funktion hat das \next nach \edef? Und wozu steht das \next am Ende?

    Gruß, Mark.

  4. #4
    Registrierter Benutzer
    Registriert seit
    19.05.2009
    Beiträge
    4.045
    \toks@=\expandafter{#1}

    Kopiert #1 in toks@, expandiert ggfs. vorher einen Befehl, der am Anfang von #1 steht, genau eine Ebene tief, d.h. aus \tmpC wird scale=\tmpA.

    \edef\next{\noexpand\setkeys{TEST}{\the\toks@}}

    Definiert \next, expandiert dabei das Argument. Normale Befehle werden komplett expandiert, außer es wird mit \noexpand verhindert. \the\toks@ gibt den Inhalt des toks-Registers aus (der Inhalt wird nicht weiter expandiert).

    D.h. \next ist nun definiert als \setkeys{TEST}{scale=\tmpA}.

    \show\next zeigt die Definition von \next (am Terminal oder in der log-Datei) und ist hier nur des Debuggens wegen.

    \next am Ende führt einfach die Definition von \next aus, d.h. das gewünschte
    \setkeys{TEST}{scale=\tmpA}.

  5. #5
    Registrierter Benutzer
    Registriert seit
    26.02.2010
    Beiträge
    7
    Hallo Ulrike,
    vielen Dank für die Erläuterung. Wenn man es erstmal verstanden hat ist es ganz leicht ...
    Gruß, Mark.

  6. #6
    Registrierter Benutzer
    Registriert seit
    26.02.2010
    Beiträge
    7

    Question

    Hallo!

    Ich habe Ulrikes Vorschläge umgesetzt und dachte eigentlich, ich hätte alles verstanden. Das war leider ein Irrtum! Im angehängten Beispiel habe ich quasi einen wrapper für \includegraphics gebastelt. Ist so natürlich noch unsinnig, aber die Key-Value-Liste soll noch erweitert werden.

    Im ersten Abschnitt des Beispiels (Zeilen 6-45) werden die Schlüssel für
    die Eigenschaften "angle", "scale", etc. und Makros zum Speichern der Werte angelegt, also z.B. \KV@ADOS@scale und \ADOS@scale. Außerdem werden hier zwei Makros zum Resetieren (\adosresetkeys) und Setzen (\adossetkeys) der Werte definiert. Das funktioniert dank Ulrikes Hilfe bereits.

    Ich zweiten Abschnitt (Zeilen 47-68) werrden zwei neue Token angelegt,
    in denen ich eine neue Version der Key-Value-Liste anlegen kann, um später unerwünschte Schlüssel aus der Liste zu entfernen. Hierzu benötige ich ein Makro \adosunifytoks (aus: N. Schwarz "Einführung in TEX"), um zwei token zu verbinden. Mit \adostokscmd möchte ich einen Schlüssel und einen Wert in die neue Liste kopieren. Hierzu wird zunächst der Wert ermittelt und mit dem default-Wert verglichen. Unterscheiden sich beide, soll ein temporärer token mit z.B. {scale=0.8} erzeugt werden und an die neue Liste angehängt werden.
    Leider funktioniert das nur fast (siehe unten).

    Im dritten Abschnitt (Zeilen 70-103) werden zwei Makros definiert: \adosincludegraphics bindet ein Bild ein und weist dabei die neu Key-Value-Liste korrekt zu. Aufgerufen wird dieses Makro aus \adosfigure heraus.
    Diese Makro soll mal eine Abkürzung für einen Aufruf der figure-Umgebung mit Einbindung des Bildes, Bildunterschrift, Label etc. werden. Z.Z. lese ich hier nur die übergebene Key-Value-Liste mit \adossetkeys aus (funktioniert!) und
    baue mir eine neue Key-Value-Liste auf. Dann binde ich ein Bild mit \adosincludegraphics ein.

    Im letzen Abschnitt (Zeilen 105ff) werden zwei Bilder mit \adosfigure eingebunden und der Aufbau der neuen Key-Value-Liste geprüft.

    Im Moment habe ich folgende Schwierigkeiten:

    1)
    Ich kann mit meinem wrapper die keys "angle", "scale", "width" und "height" (Test 1) korrekt verwenden, allerdings nicht z.B. "draft" (Test 2). Hat vermutlich etwas mit dem nächsten Problem zu tun.

    2)
    Ich verstehe noch nicht, wie ich ein Makro veranlassen kann, sich vollständig zu expandieren.

    Mit \adossetkeys[scale=0.8] (in Test 3) setzte ich \ADOS@scale auf 0.8. Ich kann diesen Wert mit \show\ADOS@scale sehen. Wenn ich \adostokscmd{scale}{1} aufrufe, wird ein Eintrag "scale=0.8" im Token \toksADOSlist erzeugt. Wenn ich aber genau hinsehe steht da eigentlich: "scale=\adoskeyval{ADOS}{scale}". Ich würde gerne Zeile 64 durch \toksADOStmp={#1=\ADOS@value,}% ersetzen. Leider steht dann im Token "scale=\ADOS@value" und nicht "scale=0.8". Was kann ich tun?

    Beste Grüße, Mark.

    Code:
    \documentclass{article}
    
    \usepackage{graphicx}
    \usepackage{ifthen}
    
    % ======================================================================
    % KEYS
    % ======================================================================
    \makeatletter
    % macros: graphics
    \newcommand{\ADOS@angle}{}		% angle=...
    \newcommand{\ADOS@width}{}		% width=...
    \newcommand{\ADOS@height}{}		% height=...
    \newcommand{\ADOS@scale}{}		% scale=...
    \newcommand{\ADOS@origin}{}		% origin=...	
    \newcommand{\ADOS@clip}{}		% clip=...
    \newcommand{\ADOS@draft}{}		% draft=...
    \newcommand{\ADOS@bb}{}		% bb=...
    % keys: graphics	
    \define@key{ADOS}{angle}[0]{\renewcommand{\ADOS@angle}{#1}}
    \define@key{ADOS}{width}[]{\renewcommand{\ADOS@width}{#1}}
    \define@key{ADOS}{height}[]{\renewcommand{\ADOS@height}{#1}}
    \define@key{ADOS}{scale}[1]{\renewcommand{\ADOS@scale}{#1}}
    \define@key{ADOS}{origin}[]{\renewcommand{\ADOS@origin}{#1}}
    \define@key{ADOS}{clip}[true]{\renewcommand{\ADOS@clip}{#1}}
    \define@key{ADOS}{draft}[false]{\renewcommand{\ADOS@draft}{#1}}
    \define@key{ADOS}{bb}[]{\renewcommand{\ADOS@bb}{#1}}
    % command: reset default keys
    \newcommand{\adosresetkeys}{%
    	% default keys
    	\setkeys{ADOS}{angle,scale,clip,draft}
    	\renewcommand{\ADOS@width}{}		% width=...
    	\renewcommand{\ADOS@height}{}		% height=...
    	\renewcommand{\ADOS@origin}{}		% origin=...
    	\renewcommand{\ADOS@bb}{}		% bb=...				
    }%
    % command: set keys
    \newcommand{\adossetkeys}[1][]{%	  				
    	\adosresetkeys
    	\toks@=\expandafter{#1}%  	
    	\edef\next{\noexpand\setkeys{ADOS}{\the\toks@}}%
    	\show\next%  	  	
    	\next%
    }%
    \makeatother
    
    % ======================================================================
    % TOKEN
    % ======================================================================
    % new tokens
    \newtoks\toksADOStmp
    \newtoks\toksADOSlist
    % command: unify tokens 
    \def\adosunifytoks(#1,#2){\expandafter\expandafter\expandafter{\expandafter\the\expandafter#1\the#2}}
    % command: get key value
    \def\adoskeyval#1#2{\csname#1@#2\endcsname}
    % command: add key-value pair to token list
    \makeatletter
    \newcommand{\adostokscmd}[2]{%
    	\edef\ADOS@value{\adoskeyval{ADOS}{#1}}%	
    	% if key-value != default
    	\ifthenelse{\equal{\ADOS@value}{#2}}{}{%		
    		\show\ADOS@value%				
    		\toksADOStmp={#1=\adoskeyval{ADOS}{#1},}%
    		\toksADOSlist=\adosunifytoks(\toksADOSlist,\toksADOStmp)%
    	}%
    }%
    \makeatother
    
    % ======================================================================
    % FIGURE
    % ======================================================================
    % command: include graphics
    \makeatletter
    \newcommand{\adosincludegraphics}[2][]{%			
    	\toks@=\expandafter{#1}%
    	\edef\next{\noexpand\includegraphics[\the\toks@]{#2}}%
    	\show\next%
    	\next%
    }%
    \makeatother
    % command: new figure environment
    \newcommand{\adosfigure}[5][]{%
      % keys				
    	\adossetkeys[#1]
    	\toksADOSlist={}
    	\adostokscmd{angle}{0}
    	\adostokscmd{width}{}
    	\adostokscmd{height}{}
    	\adostokscmd{scale}{1}
    	\adostokscmd{origin}{}
    	\adostokscmd{clip}{true}
    	\adostokscmd{draft}{false}
    	\adostokscmd{bb}{}
    	% setup
    	% ...		
    	% include graphics
    	\adosincludegraphics[\the\toksADOSlist]{#2}
    	% caption
    	% ...
    	% label
    	% ...
    }%
    
    % ======================================================================
    % DOCUMENT
    % ======================================================================
    
    \begin{document}
    	
    	% Test 1: funktioniert
    	Test~1:\\
    	\adosfigure[angle=20,width=2cm,height=4cm]{bild}{long caption}{short caption}{}\\
    	Keys: \the\toksADOSlist \\
    	
    	% Test 2: funktioniert nicht
    	%\adosfigure[angle=20,width=2cm,draft=true]{bild}{long caption}{short caption}{}
    	
    	% Test 3: funktioniert fast	
    	Test~3:\\
    	\adossetkeys[scale=0.8]
    	\toksADOSlist={}
    	\adostokscmd{scale}{1}
    	Keys: \the\toksADOSlist
    \end{document}

  7. #7
    Registrierter Benutzer
    Registriert seit
    19.05.2009
    Beiträge
    4.045
    Ich habe jetzt keine Zeit mich da durchzuwühlen.

    Wenn du einen Befehl komplett expandieren willst, nimm nicht \expandafter oder toks-Register, sondern schlicht \edef.

    Ansonsten würde ich dir empfehlen, deinen Code erstmal mit einen oder zwei Schlüsseln zu testen. Das würde ihn viel übersichtlicher machen.

Stichworte

Lesezeichen

Berechtigungen

  • Neue Themen erstellen: Nein
  • Themen beantworten: Nein
  • Anhänge hochladen: Nein
  • Beiträge bearbeiten: Nein
  •