温馨提示:本文翻译自stackoverflow.com,查看原文请点击:其他 - Difficult workflow writing Latex book full of Python code
ipython jupyter latex python workflow

其他 - 编写Python完整的Latex书籍的工作流程困难

发布于 2020-04-16 12:20:08

我正在写一本关于使用Latex用python编码的书。我计划在整个文本中穿插许多文本,并附上其输出。真正给我带来麻烦的是,当我需要返回并编辑我的python代码时,将其很好地返回到我的最新文档中是一个巨大的痛苦。

我已经做了很多研究,但似乎找不到一个好的解决方案。

这一个包含完整文件,不能解决我的问题 https://tex.stackexchange.com/questions/289385/workflow-for-includes-jupyter-aka-ipython-notebooks-as-pages-in-a-乳胶

与此相同。 http://blog.juliusschulz.de/blog/ultimate-ipython-notebook

找到解决方案1(糟糕)

我可以使用清单Latex包将python代码复制并粘贴到Latex中。

优点:

  1. 易于更新的一小段代码。

缺点:

  1. 对于需要在python中运行的输出,请分别复制,粘贴。
  2. 最初编写SLOW时,每章需要执行数百次此过程。

找到解决方案2(错误)

使用带有降价标记的jupyter笔记本,导出到Latex,将\ include文件导出到Latex主文档中。

优点:

  1. 精简
  2. 内含输出。

缺点:

  • 要进行小的更改,需要重新导入整个文档,不会保存对Latex编辑器中的markdown文本所做的任何更改
  • 在jupyter笔记本中重命名python中的单个变量可能需要几个小时。
  • 编辑似乎是一项繁琐的工作。

理想的解决方案

  • 用乳胶写文字
  • 在jupyter笔记本中编写python,导出到乳胶中。
  • 以某种方式将代码片段(导出文件的一小部分)包含在主乳胶书的不同部分中。这是我不知道的部分
  • 当需要更改python时,请在jupyter中进行更改,然后重新导出为具有相同名称的乳胶文件
  • 乳胶书会自动从包含中更新。

这里的关键是导出的python笔记本将被拆分并发送到文档的不同部分。为了使其正常工作,需要以某种方式在笔记本的降价或代码中对其进行标记或标记,因此当我重新导出笔记本时,这些相同的部分将被发送到书中的相同位置。

优点:

  1. Python编辑容易,易于传播回书本。
  2. 用乳胶书写的文字,可以使用乳胶的力量

非常感谢您提出与我的理想解决方案更接近的解决方案。这太痛苦了。

可能没关系,但是我用VS Code编写了乳胶和jupyter笔记本。如果愿意解决这些问题,我愿意更换工具。

查看更多

提问者
Adam B
被浏览
59
ymonad 2019-03-09 21:32

这是我写的一个小脚本。它拆分单个*.ipynb文件并将其转换为多个*.tex文件。

用法是:

  1. 复制以下脚本并将其另存为 main.py
  2. 执行python main.py init它将创建main.texstyle_ipython_custom.tplx
  3. 在您的Jupyther笔记本中,向要提取的每个单元格添加多余的行#latex:tag_a#latex:tag_b..。相同的标签将被提取到相同的*.tex文件中。
  4. 将其另存为*.ipynb文件。幸运的是,当前的VSCode python插件支持导出到*.ipynb,或使用jupytext从转换*.py*.ipynb
  5. 运行python main.py path/to/your.ipynb,它将创建tag_a.textag_b.tex
  6. 编辑main.tex并添加\input{tag_a.tex}\input{tag_b.tex}在任何需要的地方。
  7. 运行pdflatex main.tex,它将产生main.pdf

该脚本背后的想法是:

使用默认值从jupyter笔记本转换为LaTex nbconvert.LatexExporter会生成完整的LaTex文件,其中包含宏定义。使用它转换每个单元格可能会创建大型LaTex文件。为避免此问题,脚本首先创建main.tex仅具有宏定义的脚本,然后将每个单元格转换为不具有宏定义的LaTex文件。可以使用自定义模板文件完成此操作,style_ipython.tplx

标记或标记单元格可能是使用单元格元数据完成的,但我找不到如何在VSCode python插件(Issue)中进行设置的方法,因此它使用regex模式扫描每个单元格的源^#latex:(.*),并在将其转换为LaTex文件之前将其删除。 。

资源:

import sys
import re
import os
from collections import defaultdict
import nbformat
from nbconvert import LatexExporter, exporters

OUTPUT_FILES_DIR = './images'
CUSTOM_TEMPLATE = 'style_ipython_custom.tplx'
MAIN_TEX = 'main.tex'


def create_main():
    # creates `main.tex` which only has macro definition
    latex_exporter = LatexExporter()
    book = nbformat.v4.new_notebook()
    book.cells.append(
        nbformat.v4.new_raw_cell(r'\input{__your_input__here.tex}'))
    (body, _) = latex_exporter.from_notebook_node(book)
    with open(MAIN_TEX, 'x') as fout:
        fout.write(body)
    print("created:", MAIN_TEX)


def init():
    create_main()
    latex_exporter = LatexExporter()
    # copy `style_ipython.tplx` in `nbconvert.exporters` module to current directory,
    # and modify it so that it does not contain macro definition
    tmpl_path = os.path.join(
        os.path.dirname(exporters.__file__),
        latex_exporter.default_template_path)
    src = os.path.join(tmpl_path, 'style_ipython.tplx')
    target = CUSTOM_TEMPLATE
    with open(src) as fsrc:
        with open(target, 'w') as ftarget:
            for line in fsrc:
                # replace the line so than it does not contain macro definition
                if line == "((*- extends 'base.tplx' -*))\n":
                    line = "((*- extends 'document_contents.tplx' -*))\n"
                ftarget.write(line)
    print("created:", CUSTOM_TEMPLATE)


def group_cells(note):
    # scan the cell source for tag with regexp `^#latex:(.*)`
    # if sames tags are found group it to same list
    pattern = re.compile(r'^#latex:(.*?)$(\n?)', re.M)
    group = defaultdict(list)
    for num, cell in enumerate(note.cells):
        m = pattern.search(cell.source)
        if m:
            tag = m.group(1).strip()
            # remove the line which contains tag
            cell.source = cell.source[:m.start(0)] + cell.source[m.end(0):]
            group[tag].append(cell)
        else:
            print("tag not found in cell number {}. ignore".format(num + 1))
    return group


def doit():
    with open(sys.argv[1]) as f:
        note = nbformat.read(f, as_version=4)
    group = group_cells(note)
    latex_exporter = LatexExporter()
    # use the template which does not contain LaTex macro definition
    latex_exporter.template_file = CUSTOM_TEMPLATE
    try:
        os.mkdir(OUTPUT_FILES_DIR)
    except FileExistsError:
        pass
    for (tag, g) in group.items():
        book = nbformat.v4.new_notebook()
        book.cells.extend(g)
        # unique_key will be prefix of image
        (body, resources) = latex_exporter.from_notebook_node(
            book,
            resources={
                'output_files_dir': OUTPUT_FILES_DIR,
                'unique_key': tag
            })
        ofile = tag + '.tex'
        with open(ofile, 'w') as fout:
            fout.write(body)
            print("created:", ofile)
        # the image data which is embedded as base64 in notebook
        # will be decoded and returned in `resources`, so write it to file
        for filename, data in resources.get('outputs', {}).items():
            with open(filename, 'wb') as fres:
                fres.write(data)
                print("created:", filename)


if len(sys.argv) <= 1:
    print("USAGE: this_script [init|yourfile.ipynb]")
elif sys.argv[1] == "init":
    init()
else:
    doit()