采用网络标准

我们开发当前Notebook电脑Web应用程序的主要原因之一是采用了Web技术。

通过使用HTML、JavaScript和CSS作为纯Web应用程序,Notebook电脑可以免费获得所有Web技术的改进。因此,随着浏览器对不同媒体的支持的扩展,Notebook网络应用程序应该能够在不进行修改的情况下兼容。

随着JavaScriptVM速度的提高,用户界面的性能也是如此。

仅使用Web技术的另一个优点是,最终用户可以完全访问接口的代码,并且可以实时修改。即使这项任务并不总是简单的,我们也努力使代码尽可能地可访问和可重用。这应该允许我们以最小的努力开发定制Web界面行为的小扩展。

篡改Notebook应用程序

您可以使用的第一个工具是浏览器“开发人员工具”。确切的命名可能会随着浏览器的不同而改变,可能需要安装扩展。但基本上,它们允许您检查/修改DOM,并与运行前端的JavaScript代码交互。

  • 在Chrome和Safari中,开发人员工具在菜单中 View > Developer > JavaScript Console

  • 在Firefox中,您可能需要安装 Firebug

这些将是您调试和尝试不同扩展方法的最好朋友。

注射JS

使用魔法

上面的工具在编辑长的javascript文件时可能很繁琐。因此,我们提供 %%javascript 魔术。这允许您快速将javascript注入到Notebook中。不过,以这种方式注入的javascript在重新加载后仍然无法生存。因此,它是一个测试和精炼脚本的好工具。

你可能会看到这里或那里有人通过读取文件并将其发布到Notebook中来修改CSS和向Notebook中注入JS。这不仅经常打断Notebook的流程,使Notebook的重新执行中断,而且还意味着每次需要更新代码时,都需要在整个Notebook中执行这些单元格。

这在某些情况下仍然有用,例如 %autosave 可以控制每次保存之间的时间的魔法。但是可以用一个javascript下拉菜单替换它来选择保存间隔。

[ ]:
## you can inspect the autosave code to see what it does.
%autosave??

custom.js

为了注入javascript,我们提供了一个入口点: custom.js 允许用户执行其他资源并将其加载到Notebook中。javascript代码输入 custom.js 将在Notebook应用程序启动时执行,然后可用于自定义用户界面和Notebook行为中的几乎任何内容。

custom.js 可以在 ~/.jupyter/custom/custom.js . 您可以与其他人共享您的custom.js。

回到理论
[ ]:
from jupyter_core.paths import jupyter_config_dir
jupyter_dir = jupyter_config_dir()
jupyter_dir

自定义JS在

[ ]:
import os.path
custom_js_path = os.path.join(jupyter_dir, 'custom', 'custom.js')
[ ]:
#  my custom js
if os.path.isfile(custom_js_path):
    with open(custom_js_path) as f:
        print(f.read())
else:
    print("You don't have a custom.js file")

注意 custom.js 是指由用户修改。在编写脚本时,可以在单独的文件中定义它,并将一行配置添加到 custom.js 这将获取并执行文件。

警告 :即使修改 custom.js 浏览器刷新后立即生效(除非浏览器缓存具有攻击性)。 创建 一个文件 static/ 目录需要 服务器重新启动 .

练习:

  • 创建一个 custom.js 在正确的位置,包含以下内容:

alert("hello world from custom.js")
  • 重新启动服务器并打开任何Notebook。

  • 受到custom.js的欢迎

看一看 default custom.js ,以查看其内容和更多解释。

对于快速的:

我们在上面已经看到,你可以改变自动保存率使用魔术。这通常是我不想每次都键入的内容,而且我不喜欢将其嵌入到我的工作流和文档中。(读者不在乎我的自动保存时间是多少)。让我们构建一个允许我们这样做的扩展。

在工具栏(dom)中创建下拉元素 Jupyter.toolbar.element )你需要

  • Jupyter.notebook.set_autosave_interval(milliseconds)

  • 知道1分=60秒,1秒=1000毫秒

var label = jQuery('<label/>').text('AutoScroll Limit:');
var select = jQuery('<select/>')
     //.append(jQuery('<option/>').attr('value', '2').text('2min (default)'))
     .append(jQuery('<option/>').attr('value', undefined).text('disabled'))

     // TODO:
     //the_toolbar_element.append(label)
     //the_toolbar_element.append(select);

select.change(function() {
     var val = jQuery(this).val() // val will be the value in [2]
     // TODO
     // this will be called when dropdown changes

});

var time_m = [1,5,10,15,30];
for (var i=0; i < time_m.length; i++) {
     var ts = time_m[i];
                                          //[2]   ____ this will be `val` on [1]
                                          //     |
                                          //     v
     select.append($('<option/>').attr('value', ts).text(thr+'min'));
     // this will fill up the dropdown `select` with
     // 1 min
     // 5 min
     // 10 min
     // 10 min
     // ...
}

首先是一个非交互式示例

我喜欢我的赛马拉松被很好的强调

Jupyter.config.cell_magic_highlight['magic_text/x-cython'] = {}
Jupyter.config.cell_magic_highlight['magic_text/x-cython'].reg = [/^%%cython/]

text/x-cython 是codemirror模式名称, magic_ 前缀将只是修补模式,以便包含魔术的第一行不会破坏突出显示。 reg 将触发模式更改的列表或正则表达式。

获取更多文档

遗憾的是,您必须读取JS源文件(但是有很多注释)和/或使用yuidoc构建JavaScript文档。如果你有 nodeyui-doc 安装:

$ cd ~/jupyter/notebook/notebook/static/notebook/js/
$ yuidoc . --server
warn: (yuidoc): Failed to extract port, setting to the default :3000
info: (yuidoc): Starting YUIDoc@0.3.45 using YUI@3.9.1 with NodeJS@0.10.15
info: (yuidoc): Scanning for yuidoc.json file.
info: (yuidoc): Starting YUIDoc with the following options:
info: (yuidoc):
{ port: 3000,
  nocode: false,
  paths: [ '.' ],
  server: true,
  outdir: './out' }
info: (yuidoc): Scanning for yuidoc.json file.
info: (server): Starting server: http://127.0.0.1:3000

浏览http://127.0.0.1:3000获取文档

一些方便的方法

通过浏览文档,您将看到我们有一些方便的方法,使我们能够避免每次重新发明UI:

Jupyter.toolbar.add_buttons_group([
        {
             'label'   : 'run qtconsole',
             'icon'    : 'fa-terminal', // select your icon from
                                          // http://fontawesome.io/icons/
             'callback': function(){Jupyter.notebook.kernel.execute('%qtconsole')}
        }
        // add more button here if needed.
        ]);

用一个 lot of icons 您可以从中选择。

单元元数据

最需要的功能通常是能够区分Notebook中的单个单元格,或者对其执行特定操作。为此,您可以使用 Jupyter.notebook.get_selected_cell() 或依赖 CellToolbar . 这允许您注册一组将附加到单个单元格的操作和图形元素。

单元工具栏

您可以看到通过切换 Cell Toolbar 在Notebook顶部工具栏中的选择器。它提供两个默认值 presets 那是 Defaultslideshow . 默认值允许用户手动编辑附加到每个单元格的元数据。

首先,我们定义一个函数,它首先在DOM上使用一个元素作为参数,在其中注入ui元素。第二个元素是这个元素WIS注册的单元。然后我们需要注册这个函数并给它一个名称。

注册回调

[ ]:
%%javascript
var CellToolbar = Jupyter.CellToolbar
var toggle =  function(div, cell) {
     var button_container = $(div)

     // let's create a button that shows the current value of the metadata
     var button = $('<button/>').addClass('btn btn-mini').text(String(cell.metadata.foo));

     // On click, change the metadata value and update the button label
     button.click(function(){
                 var v = cell.metadata.foo;
                 cell.metadata.foo = !v;
                 button.text(String(!v));
             })

     // add the button to the DOM div.
     button_container.append(button);
}

 // now we register the callback under the name foo to give the
 // user the ability to use it later
 CellToolbar.register_callback('tuto.foo', toggle);

注册预设

此函数现在可以是许多函数的一部分 preset 单元格工具栏的。

[ ]:
%%javascript
Jupyter.CellToolbar.register_preset('Tutorial 1',['tuto.foo','default.rawedit'])
Jupyter.CellToolbar.register_preset('Tutorial 2',['slideshow.select','tuto.foo'])

您现在应该可以访问两个预设:

  • 教程1

  • 教程2

并检查您定义的按钮在切换预设时是否共享状态。另外,请检查单击按钮时是否修改了单元格的元数据,以及在重新加载时保存元数据时是否仍然可用。

练习:

尝试将所有代码包装在一个文件中,将此文件放入 {{jupyter_dir}}/custom/<a-name>.js 并添加

require(['custom/<a-name>']);

在里面 custom.js 使此脚本自动加载到所有Notebook中。

require is provided by a JavaScript library 这允许你表达依赖性。对于与前一个扩展类似的简单扩展,我们直接将全局命名空间设为静音,但对于更复杂的扩展,可以将回调传递给 require([...], <callback>) 调用,允许用户将配置信息传递给插件。

在python语言中,

require(['a/b', 'c/d'], function( e, f){
    e.something()
    f.something()
})

可以理解为

import a.b as e
import c.d as f
e.something()
f.something()

例如,见@damianavila “ZenMode” plugin

// read that as
// import custom.zenmode.main as zenmode
require(['custom/zenmode/main'],function(zenmode){
    zenmode.background('images/back12.jpg');
})

为了最快的

尝试使用 the following 将下拉列表绑定到 cell.metadata.difficulty.select .

它应该能够取以下4个值:

  • <None>

  • Easy

  • Medium

  • Hard

我们将使用它根据每个单元格上的标记自定义已转换Notebook的输出。

[1]:
# %load soln/celldiff.js
[ ]: