Belle II Software development
b2latex.py
1#!/usr/bin/env python3
2
3
10
11import subprocess
12
13
15 """
16 Common base class of all Latex Wrapper objects
17 """
18
19 def __init__(self):
20 """
21 Constructor, initialize output with empty string
22 """
23
24 self.output = ''
25
26 def __str__(self):
27 """
28 Transform object to string, in this case, just returns .the generated latex-code
29 """
30 return self.output
31
32 def add(self):
33 """
34 Add latex-code to the output string.
35 This method is usually overriden in the subclasses
36 """
37 return self
38
39 def finish(self):
40 """
41 Finishes the generation of latex-code.
42 E.g. adds end latex-commands
43 This method is usually overriden in the subclasses
44 """
45 return self
46
47 def save(self, filename, compile=False):
48 """
49 Saves the latex-code into a file, adds preamble and end of document,
50 and compiles the code if requested.
51 @param filename latex-code is stored in this file, should end on .tex
52 @param compile compile the .tex file using pdflatex into a .pdf file
53 """
54 output = r"""
55 \documentclass[10pt,a4paper]{article}
56 \usepackage[latin1]{inputenc}
57 \usepackage[T1]{fontenc}
58 \usepackage{amsmath}
59 \usepackage{amsfonts}
60 \usepackage{amssymb}
61 \usepackage{graphicx}
62 \usepackage{caption}
63 \usepackage{lmodern}
64 \usepackage{placeins}
65 \usepackage{multicol}
66 \usepackage{tikz}
67 \usetikzlibrary{shapes.arrows,chains, positioning}
68 \usepackage{booktabs} %professional tables
69 \usepackage[left=2cm,right=2cm,top=2cm,bottom=2cm]{geometry}
70 \usepackage{microtype} %optimises spacing, needs to go after fonts
71 \usepackage{hyperref} %adds links (also in TOC), should be loaded at the very end
72 \usepackage{longtable}
73 \usepackage{color}
74 \usepackage{listings}
75
76 \definecolor{gray}{rgb}{0.4,0.4,0.4}
77 \definecolor{darkblue}{rgb}{0.0,0.0,0.6}
78 \definecolor{cyan}{rgb}{0.0,0.6,0.6}
79
80 \lstset{
81 basicstyle=\ttfamily\scriptsize,
82 columns=fullflexible,
83 showstringspaces=false,
84 commentstyle=\color{gray}\upshape
85 }
86
87 \lstdefinelanguage{XML}
88 {
89 morestring=[b]",
90 morestring=[s]{>}{<},
91 morecomment=[s]{<?}{?>},
92 stringstyle=\color{black},
93 identifierstyle=\color{darkblue},
94 keywordstyle=\color{cyan},
95 morekeywords={xmlns,version,type}% list your attributes here
96 }
97
98 \usepackage[load-configurations=abbreviations]{siunitx}
99 \makeatletter
100 % In newer versions of latex there is a problem with the calc package and tikz
101 % http://tex.stackexchange.com/questions/289551/how-to-resolve-conflict-between-versions-of-texlive-and-pgf
102 \def\pgfmathparse@#1{%
103 % Stuff for calc compatiability.
104 \let\real=\pgfmath@calc@real
105 \let\minof=\pgfmath@calc@minof
106 \let\maxof=\pgfmath@calc@maxof
107 \let\ratio=\pgfmath@calc@ratio
108 \let\widthof=\pgfmath@calc@widthof
109 \let\heightof=\pgfmath@calc@heightof
110 \let\depthof=\pgfmath@calc@depthof
111 % No (math) units yet.
112 \global\pgfmathunitsdeclaredfalse
113 \global\pgfmathmathunitsdeclaredfalse
114 % Expand expression so any reamining CSs are registers
115 % or box dimensions (i.e. |\wd|, |\ht|, |\dp|).
116 \edef\pgfmath@expression{#1}%
117 %
118 \expandafter\pgfmathparse@trynumber@loop\pgfmath@expression\pgfmath@parse@stop
119 %
120 % this here is the _real_ parser. it is invoked by
121 % \pgfmathparse@trynumber@loop if that says "this is no number"
122 %\pgfmathparse@@\pgfmath@parse@stop%
123 }
124 \makeatother
125 \begin{document}
126 """
127 output += self.output
128 output += r"\end{document}"
129
130 with open(filename, 'w') as f:
131 f.write(output)
132 if compile:
133 for i in range(0, 2):
134 ret = subprocess.call(['pdflatex', '-halt-on-error', '-interaction=nonstopmode', filename])
135 if ret != 0:
136 raise RuntimeError("pdflatex failed to create FEI summary PDF, please check.")
137 return self
138
139
141 """
142 Convinience class implementing += operator, can be used instead of raw LatexObject to collect
143 all the latex code in your project which should go into a common file.
144 """
145
146 def add(self, text=''):
147 """
148 Adds an object to the output
149 @param text string or object with implicit string conversion (like LatexObject)
150 """
151 self.output += str(text)
152 return self
153
154 def __iadd__(self, text):
155 """
156 Adds an object to the output
157 @param text string or object with implicit string conversion (like LatexObject)
158 """
159 self.addadd(text)
160 return self
161
162
164 """
165 Used for wrapping conventionel text into latex-code.
166 Has to possibility to handle python-style format-placeholders
167 """
168
169 def __init__(self, text=''):
170 """
171 Calls super-class initialize and adds initial text to output
172 @param text intial text, usually you want to give a raw string r"some text"
173 """
174 super().__init__()
175
176 self.outputoutput += str(text)
177
178 def add(self, text=''):
179 """
180 Adds an object to the output, can contain python-placeholders
181 @param text string or object with implicit string conversion (like LatexObject)
182 """
183 self.outputoutput += str(text)
184 return self
185
186 def finish(self, **kwargs):
187 """
188 Finish the generation of the string by replacing possible placehholders with the given dictionary
189 @param kwargs dictionary used to replace placeholders
190 """
191
192 self.outputoutput = self.outputoutput.format(**kwargs) + '\n'
193 return self
194
195
197 """
198 Used for wrapping code in a listing environment
199 """
200
201 def __init__(self, language='XML'):
202 """
203 Calls super-class initialize and adds initial text to output
204 @param text intial text, usually you want to give a raw string r"some text"
205 """
206 super().__init__()
207
208 self.output += r'\lstset{language=' + language + '}\n'
209 self.output += r'\begin{lstlisting}[breaklines=true]' + '\n'
210
211 def add(self, text=''):
212 """
213 Adds code to the output
214 @param code which is wrapped in the listing environment
215 """
216 self.output += str(text)
217 return self
218
219 def finish(self, **kwargs):
220 """
221 Finish the generation of the lsiting environment
222 """
223
224 self.output += r'\end{lstlisting}'
225 return self
226
227
229 """
230 Defines the colourlist latex-command, which draws a bargraph with relative
231 fractions indicated by colours using tikz.
232 After including this object in you latex code the command \\bargraph is available.
233 You should include only one of these objects in your latex code.
234 """
235
236
237 colours = ["red", "green", "blue", "orange", "cyan", "purple"]
238
239 def __init__(self):
240 """
241 Calls super-class init, adds definition of colourlist to latex code.
242 """
243 super().__init__()
244 self.output += r"\def\colourlist{{" + ', '.join(f'"{c}"' for c in self.colours) + r"}}" + '\n'
245 self.output += r"""
246 \tikzset{nodeStyle/.style={text height=\heightof{A},text depth=\depthof{g}, inner sep = 0pt, node distance = -0.15mm}}
247 \newcount\colourindex \colourindex=-1
248 \newcommand{\plotbar}[1]{
249 \begin{tikzpicture}[start chain=going right, nodes = {font=\sffamily}]
250 \global\colourindex=-1
251 \foreach \percent/\name in {
252 #1
253 } {
254 \ifx\percent\empty\else % If \percent is empty, do nothing
255 \global\advance\colourindex by 1
256 \ifnum"""
257 self.output += str(len(self.colours) - 1)
258 self.output += r"""5<\colourindex %back to first colour if we run out
259 \global\colourindex=0
260 \fi
261 \pgfmathparse{\colourlist[\the\colourindex]} % Get color from cycle list
262 \edef\color{\pgfmathresult} % and store as \color
263 \node[nodeStyle, draw, on chain, fill={\color!40}, minimum width=\percent*1.0, minimum height=12] {\name};
264 \fi
265 };
266 \end{tikzpicture}
267 }
268 """
269
270
271class Section(LatexObject):
272 """
273 Adds a new section to your latex code with some additional commands
274 to force a pagebreak and add a barrier for figure objects.
275 """
276
277 def __init__(self, name):
278 """
279 Calls super-class init and adds necessary latex commands to output.
280 """
281 super().__init__()
282 self.output += r"\raggedbottom" + '\n'
283 self.output += r"\pagebreak[0]" + '\n'
284 self.output += r"\FloatBarrier" + '\n'
285 self.output += r"\section{" + str(name) + r"}" + '\n'
286
287
289 """
290 Adds a new subsection to your latex code.
291 """
292
293 def __init__(self, name):
294 """
295 Calls super-class init and adds necessary latex commands to output.
296 """
297 super().__init__()
298 self.output += r"\subsection{" + str(name) + r"}" + '\n'
299
300
302 """
303 Adds a new subsubsection to your latex code.
304 """
305
306 def __init__(self, name):
307 """
308 Calls super-class init and adds necessary latex commands to output.
309 """
310 super().__init__()
311 self.output += r"\subsubsection{" + str(name) + r"}" + '\n'
312
313
315 """
316 Includes a series of image files into your latex code and centers them.
317 """
318
319 def __init__(self):
320 """
321 Calls super-class init and begins centered environment.
322 """
323 super().__init__()
324 self.output += r"\begin{center}" + '\n'
325
326 def add(self, filename, width=0.7):
327 """
328 Include a image file.
329 @param filename containing the image
330 @param width texwidth argument of includegraphics
331 """
332 self.output += r"\includegraphics[width=" + str(width) + r"\textwidth]"
333 self.output += r"{" + str(filename) + r"}" + '\n'
334 return self
335
336 def finish(self):
337 """
338 Ends centered environment
339 """
340 self.output += r"\end{center}" + '\n'
341 return self
342
343
345 """
346 Creates a itemized list in latex.
347 """
348
349 def __init__(self):
350 """
351 Calls super-class init and begins itemize
352 """
353 super().__init__()
354
355 self.amount = 0
356 self.output += r"\begin{itemize}"
357
358 def add(self, item):
359 """
360 Adds another item.
361 @param item string or object with implicit string conversion used as item
362 """
363 self.amount += 1
364 self.output += r"\item " + str(item) + '\n'
365 return self
366
367 def finish(self):
368 """
369 Finishes the generation of latex-code.
370 """
371 if self.amount == 0:
372 return ''
373 self.output += r"\end{itemize}"
374 return self
375
376
378 """
379 Creates a longtable in latex. A longtable can span multiple pages
380 and is automatically wrapped.
381 """
382
383 def __init__(self, columnspecs, caption, format_string, head):
384 """
385 Calls super-class init, begins centered environment and longtable environment.
386 Defines caption and head of the table.
387 @param columnspecs of the longtable, something like:
388 rclp{7cm} 4 columns, right-center-left aligned and one paragraph column with a width of 7cm
389 @param caption string or object with implicit string conversion used as caption.
390 @param format_string python-style format-string used to generate a new row out of a given dictionary.
391 @param head of the table
392 """
393 super().__init__()
394 self.output += r"\begin{center}" + '\n'
395 self.output += r"\begin{longtable}{" + str(columnspecs) + r"}" + '\n'
396 self.output += r"\caption{" + str(caption) + r"}\\" + '\n'
397 self.output += r"\toprule" + '\n'
398 self.output += head + r"\\" + '\n'
399 self.output += r"\midrule" + '\n'
400
401 self.format_string = format_string
402
403 def add(self, *args, **kwargs):
404 """
405 Add a new row to the longtable by generating the row using the format_string given in init
406 and the provided dictionary.
407 @param args positional arguments used to generate the row using the python-style format string.
408 @param kwargs dictionary used to generate the row using the python-style format-string.
409 """
410 self.output += self.format_string.format(*args, **kwargs) + r"\\" + '\n'
411 return self
412
413 def finish(self, tail=''):
414 """
415 Adds optional tail of the table, ends longtable and centered environment.
416 @param tail optional tail, like head but at the bottom of the table.
417 """
418 self.output += r"\bottomrule" + '\n'
419 if str(tail) != '':
420 self.output += str(tail) + r"\\" + '\n'
421 self.output += r"\bottomrule" + '\n'
422 self.output += r"\end{longtable}" + '\n'
423 self.output += r"\end{center}" + '\n'
424 return self
425
426
428 """
429 Creates a latex title-page and optionally abstract and table-of-contents.
430 You should include only one of these objects in your latex code.
431 """
432
433 def __init__(self, title, authors, abstract, add_table_of_contents=True, clearpage=True):
434 """
435 Sets author, date, title property, calls maketitle, optionalla adds abstract and table-of-contents.
436 @param title of the latex file.
437 @param authors of the latex file, so the person who write the corresponding python-code with this framework :-)
438 @param abstract optional abstract placed on the title-page.
439 @param add_table_of_contents bool indicating of table-of-contents should be included.
440 """
441 super().__init__()
442 self.output += r"\author{"
443 for author in authors:
444 self.output += author + r"\\"
445 self.output += r"}" + '\n'
446 self.output += r"\date{\today}" + '\n'
447 self.output += r"\title{" + title + r"}" + '\n'
448 self.output += r"\maketitle" + '\n'
449 if abstract:
450 self.output += r"\begin{abstract}" + '\n'
451 self.output += abstract
452 self.output += '\n' + r'\end{abstract}' + '\n'
453
454 if clearpage:
455 self.output += r"\clearpage" + '\n'
456 if add_table_of_contents:
457 self.output += r"\tableofcontents" + '\n'
458 self.output += r"\FloatBarrier" + '\n'
459 if clearpage:
460 self.output += r"\clearpage" + '\n'
461
462
463if __name__ == '__main__':
464 import random
465
466 o = LatexFile()
467 o += TitlePage(title='Automatic Latex Code Example',
468 authors=['Thomas Keck'],
469 abstract='This is an example for automatic latex code generation in basf2.',
470 add_table_of_contents=True).finish()
471
472 o += Section("LongTable").finish()
473 o += String(r"In the following subsection there's:")
474 o += Itemize().add("a really long table").add("a coloured table").add("nothing else").finish()
475 o += SubSection("A really long table")
476
477 table = LongTable(r"lr", "A long table", "{name} & {value:.2f}", r"Name & Value in $\mathrm{cm}$")
478 for i in range(60):
479 table.add(name='test' + str(i), value=random.gauss(0.0, 1.0))
480 o += table.finish()
481
482 o += SubSection("A table with color and tail")
483 colour_list = DefineColourList()
484 o += colour_list.finish()
485
486 table = LongTable(columnspecs=r"lcrrr",
487 caption="A coloured table for " +
488 ', '.join((f'\\textcolor{{{c}}}{{{m}}}' for c, m in zip(colour_list.colours[:3], "RGB"))),
489 format_string="{name} & {bargraph} & {r:.2f} & {g:.2f} & {b:.2f}",
490 head=r"Name & Relative fractions & R - Value & G - Value & B - Value")
491 sr = sg = sb = 0
492 for i in range(10):
493 r = random.uniform(0.1, 0.9)
494 g = random.uniform(0.1, 0.9)
495 b = random.uniform(0.1, 0.9)
496 n = (r + g + b) / 100.0
497 sr += r
498 sg += g
499 sb += b
500 table.add(name='test' + str(i), bargraph=r'\plotbar{{ {:g}/, {:g}/, {:g}/,}}'.format(r / n, g / n, b / n), r=r, g=g, b=b)
501 n = (sr + sg + sb) / 100.0
502 o += table.finish(tail=r"Total & \plotbar{{ {:g}/, {:g}/, {:g}/,}} & {:g} & {:g} & {:g}".format(sr /
503 n, sg / n, sb / n, sr, sg, sb))
504
505 o += Section("Graphic section")
506 graphics = Graphics()
507 images = subprocess.check_output("locate .png", shell=True).split('\n')[:-1]
508 for i in range(4):
509 image = random.choice(images)
510 graphics.add(image, width=0.49)
511 o += graphics.finish()
512
513 from B2Tools import format
514 o += Section("Format section e.g. " + format.decayDescriptor("B+ ==> mu+ nu gamma"))
515 o += r"Some important channels in B physics"
516 items = Itemize()
517 items.add(format.decayDescriptor("B+ ==> mu+ nu gamma"))
518 items.add(format.decayDescriptor("B0 ==> J/Psi K_S0"))
519 items.add(format.decayDescriptor("B+ ==> tau+ nu"))
520 o += items.finish()
521
522 o += r"Use format.variable to add hyphenations to long variable names, so latex can do line breaks "
523 o += format.variable("daughterProductOf(extraInfo(SignalProbability))")
524 o += r" . Use format.string for stuff that contains special latex characters like " + format.string(r"_ ^ \ ")
525 o += r" . Use format.duration for durations " + format.duration(20000) + " " + format.duration(0.00010)
526
527 o.finish()
528 o.save("test.tex", compile=True)
list colours
6 default colours used for the bargraph
Definition: b2latex.py:237
def add(self, filename, width=0.7)
Definition: b2latex.py:326
def add(self, item)
Definition: b2latex.py:358
amount
number of items
Definition: b2latex.py:355
def add(self, text='')
Definition: b2latex.py:146
def __iadd__(self, text)
Definition: b2latex.py:154
output
Stores the outputted latex-code.
Definition: b2latex.py:24
def save(self, filename, compile=False)
Definition: b2latex.py:47
def add(self, text='')
Definition: b2latex.py:211
def finish(self, **kwargs)
Definition: b2latex.py:219
def __init__(self, language='XML')
Definition: b2latex.py:201
def __init__(self, columnspecs, caption, format_string, head)
Definition: b2latex.py:383
def add(self, *args, **kwargs)
Definition: b2latex.py:403
def finish(self, tail='')
Definition: b2latex.py:413
format_string
python-style format-string used to generate a new row out of a given dictionary.
Definition: b2latex.py:401
def __init__(self, name)
Definition: b2latex.py:277
def add(self, text='')
Definition: b2latex.py:178
def finish(self, **kwargs)
Definition: b2latex.py:186
def __init__(self, text='')
Definition: b2latex.py:169
output
output string
Definition: b2latex.py:192
def __init__(self, name)
Definition: b2latex.py:293
def __init__(self, name)
Definition: b2latex.py:306
def __init__(self, title, authors, abstract, add_table_of_contents=True, clearpage=True)
Definition: b2latex.py:433