引言
关于如何将Juypter Notebook导出为幻灯片(基于reveal.js),之前曾经写过一篇(文章)[customize-ipynb-slides-reveal-js/]。但是前几天整理资料的时候发现,随着nbconvert
升级到6以上的版本之后,由于其模板系统发生了破坏性改动(breaking changes),之前那篇文章的方法已经不再适用。因此,针对nbconvert
6以上的版本做了些调整,与之前相同的东西不再赘述。
模板系统改动
nbconvert
6引入了新的版本系统,主要变动包括:
-
模板的后缀名从tpl
改为了j2
,但是实际还是jinja2语法;
-
自带模板的路径安装在installation prefix>/share/jupyter/nbconvert
-
在定义模板时,需要有一个conf.json
位于模板的根目录下,其作用为标明:
- 所继承的基本模板
- 模板的mimetypes
- 适用该模板时要在exporter中注册的预处理器类(preprocessors)
-
采用Reveal.js 4.x 版本,并且可以用HTML exporter导出:
1
| jupyter nbconvert <path-to-notebook> --to html --template reveal
|
注:目前--to slides
仍然适用。
自定义模板
假设我们自定义模板的名称为my_slides_template
,其目录结构如下所示:
1 2 3 4 5 6 7 8 9 10 11 12 13
| . |-- base.html.j2 |-- conf.json |-- index.html.j2 `-- static |-- custom_reveal.css |-- plugin | `-- customcontrols | |-- plugin.js | `-- style.css `-- zenburn.css
3 directories, 7 files
|
我首先复制了nbconvert自带的reveal模板,然后在此基础上进行修改。
Cell的元数据修改
通过为Juypter Notebook的Cell增加自定义的元数据(metadata),从而控制其导出时的class属性。
这一部分通过修改base.html.j2
实现。
1 2 3 4 5 6 7 8 9
| {%- if cell.metadata.get('fragment_start', False) -%} {# 控制幻灯片是否可见 #} <div class="fragment {{'current-visible' if cell.metadata.get('current_visible', False)}}"> {%- endif -%}
{# 首页幻灯片 #} {%- if cell.metadata.get('homepage', False) -%} <div class="homepage"> {%- endif -%}
|
对模板主入口的修改
index.html.j2
是模板的主入口,也就是exporter通过读取index.html.j2
,然后进行解析和导出。
导入外部文件
主要是样式表:
1 2 3 4 5
| <!-- General and theme style sheets --> <link rel="stylesheet" href="{{ reveal_url_prefix }}/dist/reveal.css"> <link rel="stylesheet" href="{{ reveal_url_prefix }}/dist/theme/{{reveal_theme}}.css" id="theme"> <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/reveal.js-plugins/menu/font-awesome/css/fontawesome.css"> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.2.1/css/all.min.css" integrity="sha512-MV7K8+y+gLIBoVD59lQIYicR65iaqukzvf/nwasF0nqhPay5w/9lJmVM2hMDcnK1OnMGCdVK+iQrJ7lzPJQd1w==" crossorigin="anonymous" referrerpolicy="no-referrer" />
|
导入静态文件
导入static
文件夹中的文件:
1 2 3 4
| {{ resources.include_css("static/custom_reveal.css") }} {{ resources.include_css("static/zenburn.css") }} {{ resources.include_css("static/plugin/customcontrols/style.css") }} {{ resources.include_js("static/plugin/customcontrols/plugin.js") }}
|
添加页眉和页脚
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| {%- block body_header -%} {% if resources.theme == 'dark' %} <body class="jp-Notebook" data-jp-theme-light="false" data-jp-theme-name="JupyterLab Dark"> {% else %} <body class="jp-Notebook" data-jp-theme-light="true" data-jp-theme-name="JupyterLab Light"> {% endif %} {# 页眉 #} <div class="headbar"> {{nb.metadata.get('headbar', '')}} </div> {# 页脚 #} <div class="footer"> Copyright ©2022 <a href="https://www.northfar.net">Zhang Tongshuai</a> </div> <div class="reveal"> <div class="slides"> {%- endblock body_header -%}
|
设置Reveal参数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| function(Reveal, RevealNotes){ window.Reveal = Reveal Reveal.initialize({ width: 1600, height: 1000, margin: 0.01, controls: true, progress: true, history: true, mouseWheel: true, slideNumber: 'c/t', transition: "{{reveal_transition}}", customcontrols: { controls: [ { id: 'toggle-overview', title: 'Toggle overview (O)', icon: '<i class="fa fa-th"></i>', action: 'Reveal.toggleOverview();' } ] }, plugins: [RevealNotes, RevealCustomControls] });
var update = function(event){ if(MathJax.Hub.getAllJax(Reveal.getCurrentSlide())){ MathJax.Hub.Rerender(Reveal.getCurrentSlide()); } }; }
|
结论
以上就是在nbconvert
6.x下自定义Reveal模板的主要调整。
参考资料
- https://blog.jupyter.org/the-templating-system-of-nbconvert-6-47ea781eacd2
- https://nbconvert.readthedocs.io/en/latest/changelog.html
- https://github.com/jupyter/nbconvert/issues/1369
- https://nbconvert.readthedocs.io/en/latest/customizing.html