bokeh.core.templates#

提供 Bokeh 用于以各种方式嵌入 Bokeh 文档和模型的 Jinja2 模板。

AUTOLOAD_JS = <Template 'autoload_js.js'>#
模板:autoload_js.js
'use strict';

(function(root) {
  function now() {
    return new Date();
  }

  const force = {{ force|default(False)|tojson }};

  if (typeof root._bokeh_onload_callbacks === "undefined" || force === true) {
    root._bokeh_onload_callbacks = [];
    root._bokeh_is_loading = undefined;
  }

  {% block register_mimetype %}
  {% endblock %}

  {% block autoload_init %}
    {%- if elementid -%}
    const element = document.getElementById({{ elementid|tojson }});
    if (element == null) {
      console.warn("Bokeh: autoload.js configured with elementid '{{ elementid }}' but no matching script tag was found.")
    }
    {%- endif %}
  {% endblock %}

  function run_callbacks() {
    try {
      root._bokeh_onload_callbacks.forEach(function(callback) {
        if (callback != null)
          callback();
      });
    } finally {
      delete root._bokeh_onload_callbacks
    }
    console.debug("Bokeh: all callbacks have finished");
  }

  function load_libs(css_urls, js_urls, callback) {
    if (css_urls == null) css_urls = [];
    if (js_urls == null) js_urls = [];

    root._bokeh_onload_callbacks.push(callback);
    if (root._bokeh_is_loading > 0) {
      console.debug("Bokeh: BokehJS is being loaded, scheduling callback at", now());
      return null;
    }
    if (js_urls == null || js_urls.length === 0) {
      run_callbacks();
      return null;
    }
    console.debug("Bokeh: BokehJS not loaded, scheduling load and callback at", now());
    root._bokeh_is_loading = css_urls.length + js_urls.length;

    function on_load() {
      root._bokeh_is_loading--;
      if (root._bokeh_is_loading === 0) {
        console.debug("Bokeh: all BokehJS libraries/stylesheets loaded");
        run_callbacks()
      }
    }

    function on_error(url) {
      console.error("failed to load " + url);
    }

    for (let i = 0; i < css_urls.length; i++) {
      const url = css_urls[i];
      const element = document.createElement("link");
      element.onload = on_load;
      element.onerror = on_error.bind(null, url);
      element.rel = "stylesheet";
      element.type = "text/css";
      element.href = url;
      console.debug("Bokeh: injecting link tag for BokehJS stylesheet: ", url);
      document.body.appendChild(element);
    }

    for (let i = 0; i < js_urls.length; i++) {
      const url = js_urls[i];
      const element = document.createElement('script');
      element.onload = on_load;
      element.onerror = on_error.bind(null, url);
      element.async = false;
      element.src = url;
      console.debug("Bokeh: injecting script tag for BokehJS library: ", url);
      document.head.appendChild(element);
    }
  };

  function inject_raw_css(css) {
    const element = document.createElement("style");
    element.appendChild(document.createTextNode(css));
    document.body.appendChild(element);
  }

  {% if bundle %}
  const js_urls = {{ bundle.js_urls|map(attribute="url")|list|tojson }};
  const css_urls = {{ bundle.css_urls|map(attribute="url")|list|tojson }};
  {% else %}
  const js_urls = {{ js_urls|tojson }};
  const css_urls = {{ css_urls|tojson }};
  {% endif %}

  const inline_js = [
    {%- for css in (bundle.css_raw if bundle else css_raw) %}
    function(Bokeh) {
      inject_raw_css({{ css|tojson }});
    },
    {%- endfor %}
    {%- for js in (bundle.js_raw if bundle else js_raw) %}
    function(Bokeh) {
      {{ js|indent(6) }}
    },
    {% endfor -%}
    function(Bokeh) {
    {% block inline_js %}
    {% endblock %}
    }
  ];

  function run_inline_js() {
    {% block run_inline_js %}
    for (let i = 0; i < inline_js.length; i++) {
      inline_js[i].call(root, root.Bokeh);
    }
    {% endblock %}
  }

  if (root._bokeh_is_loading === 0) {
    console.debug("Bokeh: BokehJS loaded, going straight to plotting");
    run_inline_js();
  } else {
    load_libs(css_urls, js_urls, function() {
      console.debug("Bokeh: BokehJS plotting callback run at", now());
      run_inline_js();
    });
  }
}(window));
AUTOLOAD_NB_JS = <Template 'autoload_nb_js.js'>#
模板:autoload_nb_js.js
{% extends "autoload_js.js" %}

{% block register_mimetype %}

  {%- if register_mime -%}
  const JS_MIME_TYPE = 'application/javascript';
  const HTML_MIME_TYPE = 'text/html';
  const EXEC_MIME_TYPE = 'application/vnd.bokehjs_exec.v0+json';
  const CLASS_NAME = 'output_bokeh rendered_html';

  /**
   * Render data to the DOM node
   */
  function render(props, node) {
    const script = document.createElement("script");
    node.appendChild(script);
  }

  /**
   * Handle when an output is cleared or removed
   */
  function handleClearOutput(event, handle) {
    function drop(id) {
      const view = Bokeh.index.get_by_id(id)
      if (view != null) {
        view.model.document.clear()
        Bokeh.index.delete(view)
      }
    }

    const cell = handle.cell;

    const id = cell.output_area._bokeh_element_id;
    const server_id = cell.output_area._bokeh_server_id;

    // Clean up Bokeh references
    if (id != null) {
      drop(id)
    }

    if (server_id !== undefined) {
      // Clean up Bokeh references
      const cmd_clean = "from bokeh.io.state import curstate; print(curstate().uuid_to_server['" + server_id + "'].get_sessions()[0].document.roots[0]._id)";
      cell.notebook.kernel.execute(cmd_clean, {
        iopub: {
          output: function(msg) {
            const id = msg.content.text.trim()
            drop(id)
          }
        }
      });
      // Destroy server and session
      const cmd_destroy = "import bokeh.io.notebook as ion; ion.destroy_server('" + server_id + "')";
      cell.notebook.kernel.execute(cmd_destroy);
    }
  }

  /**
   * Handle when a new output is added
   */
  function handleAddOutput(event, handle) {
    const output_area = handle.output_area;
    const output = handle.output;

    // limit handleAddOutput to display_data with EXEC_MIME_TYPE content only
    if ((output.output_type != "display_data") || (!Object.prototype.hasOwnProperty.call(output.data, EXEC_MIME_TYPE))) {
      return
    }

    const toinsert = output_area.element.find("." + CLASS_NAME.split(' ')[0]);

    if (output.metadata[EXEC_MIME_TYPE]["id"] !== undefined) {
      toinsert[toinsert.length - 1].firstChild.textContent = output.data[JS_MIME_TYPE];
      // store reference to embed id on output_area
      output_area._bokeh_element_id = output.metadata[EXEC_MIME_TYPE]["id"];
    }
    if (output.metadata[EXEC_MIME_TYPE]["server_id"] !== undefined) {
      const bk_div = document.createElement("div");
      bk_div.innerHTML = output.data[HTML_MIME_TYPE];
      const script_attrs = bk_div.children[0].attributes;
      for (let i = 0; i < script_attrs.length; i++) {
        toinsert[toinsert.length - 1].firstChild.setAttribute(script_attrs[i].name, script_attrs[i].value);
        toinsert[toinsert.length - 1].firstChild.textContent = bk_div.children[0].textContent
      }
      // store reference to server id on output_area
      output_area._bokeh_server_id = output.metadata[EXEC_MIME_TYPE]["server_id"];
    }
  }

  function register_renderer(events, OutputArea) {

    function append_mime(data, metadata, element) {
      // create a DOM node to render to
      const toinsert = this.create_output_subarea(
        metadata,
        CLASS_NAME,
        EXEC_MIME_TYPE
      );
      this.keyboard_manager.register_events(toinsert);
      // Render to node
      const props = {data: data, metadata: metadata[EXEC_MIME_TYPE]};
      render(props, toinsert[toinsert.length - 1]);
      element.append(toinsert);
      return toinsert
    }

    /* Handle when an output is cleared or removed */
    events.on('clear_output.CodeCell', handleClearOutput);
    events.on('delete.Cell', handleClearOutput);

    /* Handle when a new output is added */
    events.on('output_added.OutputArea', handleAddOutput);

    /**
     * Register the mime type and append_mime function with output_area
     */
    OutputArea.prototype.register_mime_type(EXEC_MIME_TYPE, append_mime, {
      /* Is output safe? */
      safe: true,
      /* Index of renderer in `output_area.display_order` */
      index: 0
    });
  }

  // register the mime type if in Jupyter Notebook environment and previously unregistered
  if (root.Jupyter !== undefined) {
    const events = require('base/js/events');
    const OutputArea = require('notebook/js/outputarea').OutputArea;

    if (OutputArea.prototype.mime_types().indexOf(EXEC_MIME_TYPE) == -1) {
      register_renderer(events, OutputArea);
    }
  }
  {%- endif -%}

{% endblock %}

{% block autoload_init %}
  if (typeof (root._bokeh_timeout) === "undefined" || force === true) {
    root._bokeh_timeout = Date.now() + {{ timeout|default(0)|tojson }};
    root._bokeh_failed_load = false;
  }

  const NB_LOAD_WARNING = {'data': {'text/html':
     "<div style='background-color: #fdd'>\n"+
     "<p>\n"+
     "BokehJS does not appear to have successfully loaded. If loading BokehJS from CDN, this \n"+
     "may be due to a slow or bad network connection. Possible fixes:\n"+
     "</p>\n"+
     "<ul>\n"+
     "<li>re-rerun `output_notebook()` to attempt to load from CDN again, or</li>\n"+
     "<li>use INLINE resources instead, as so:</li>\n"+
     "</ul>\n"+
     "<code>\n"+
     "from bokeh.resources import INLINE\n"+
     "output_notebook(resources=INLINE)\n"+
     "</code>\n"+
     "</div>"}};

  function display_loaded(error = null) {
    const el = document.getElementById({{ elementid|tojson }});
    if (el != null) {
      const html = (() => {
        if (typeof root.Bokeh === "undefined") {
          if (error == null) {
            return "BokehJS is loading ...";
          } else {
            return "BokehJS failed to load.";
          }
        } else {
          const prefix = `BokehJS ${root.Bokeh.version}`;
          if (error == null) {
            return `${prefix} successfully loaded.`;
          } else {
            return `${prefix} <b>encountered errors</b> while loading and may not function as expected.`;
          }
        }
      })();
      el.innerHTML = html;

      if (error != null) {
        const wrapper = document.createElement("div");
        wrapper.style.overflow = "auto";
        wrapper.style.height = "5em";
        wrapper.style.resize = "vertical";
        const content = document.createElement("div");
        content.style.fontFamily = "monospace";
        content.style.whiteSpace = "pre-wrap";
        content.style.backgroundColor = "rgb(255, 221, 221)";
        content.textContent = error.stack ?? error.toString();
        wrapper.append(content);
        el.append(wrapper);
      }
    } else if (Date.now() < root._bokeh_timeout) {
      setTimeout(() => display_loaded(error), 100);
    }
  }
{% endblock %}

{% block run_inline_js %}
    if (root.Bokeh !== undefined || force === true) {
      try {
        {{ super() }}
      } catch (error) {
        {%- if elementid -%}
        display_loaded(error);
        {%- endif -%}
        throw error;
      }
      {%- if elementid -%}
      if (force === true) {
        display_loaded();
      }
      {%- endif -%}
    } else if (Date.now() < root._bokeh_timeout) {
      setTimeout(run_inline_js, 100);
    } else if (!root._bokeh_failed_load) {
      console.log("Bokeh: BokehJS failed to load within specified timeout.");
      root._bokeh_failed_load = true;
    } else if (force !== true) {
      const cell = $(document.getElementById({{ elementid|tojson }})).parents('.cell').data().cell;
      cell.output_area.append_execute_result(NB_LOAD_WARNING)
    }
{% endblock %}
AUTOLOAD_REQUEST_TAG = <Template 'autoload_request_tag.html'>#

渲染 <script> 标签,这些标签会自动加载 BokehJS(如果需要),然后渲染 Bokeh 模型或文档。文档可以指定为嵌入的文档 ID 或服务器会话 ID。如果没有指定特定的模型 ID,则会渲染整个文档。

参数:
  • src_path (str) – AUTOLOAD 脚本的路径

  • elementid (str) – 脚本标签的唯一 ID

  • modelid (str) – 要渲染的对象的 Bokeh 模型 ID(如果缺少,则渲染整个文档;如果存在,则仅渲染一个模型)

  • docid (str) – 要渲染的嵌入式 Bokeh 文档的 ID

  • headers (dict) – 要包含在请求中的 HTTP 请求标头的字典

注意

此脚本在适当位置注入一个 <div>,因此必须放在 <body> 下。

模板:autoload_request_tag.html
<script id="{{ elementid }}">
  (function() {
    const xhr = new XMLHttpRequest()
    xhr.responseType = 'blob';
    xhr.open('GET', "{{ src_path }}", true);
    {% for header, value in headers.items() %}
    xhr.setRequestHeader("{{ header }}", "{{ value }}")
    {% endfor %}
    xhr.withCredentials = {{ with_credentials | tojson }};
    xhr.onload = function (event) {
      const script = document.createElement('script');
      const src = URL.createObjectURL(event.target.response);
      script.src = src;
      document.body.appendChild(script);
    };
    xhr.send();
  })();
</script>
AUTOLOAD_TAG = <Template 'autoload_tag.html'>#

渲染 <script> 标签,这些标签会自动加载 BokehJS(如果需要),然后渲染 Bokeh 模型或文档。文档可以指定为嵌入的文档 ID 或服务器会话 ID。如果没有指定特定的模型 ID,则会渲染整个文档。

参数:
  • src_path (str) – AUTOLOAD 脚本的路径

  • elementid (str) – 脚本标签的唯一 ID

  • modelid (str) – 要渲染的对象的 Bokeh 模型 ID(如果缺少,则渲染整个文档;如果存在,则仅渲染一个模型)

  • docid (str) – 要渲染的嵌入式 Bokeh 文档的 ID

注意

此脚本在适当位置注入一个 <div>,因此必须放在 <body> 下。

模板:autoload_tag.html
<script src="{{ src_path }}" id="{{ elementid }}"></script>
CSS_RESOURCES = <Template 'css_resources.html'>#

渲染根据 Resources 对象中的配置加载 Bokeh CSS 的 HTML。

参数:
  • css_files (list[str]) – 要包含的 CSS 文件的 URI 列表

  • css_raw (list[str]) – 要放在 <style> 标签之间的原始 CSS 代码段列表

模板:css_resources.html
{% for file in css_files %}
    <link rel="stylesheet" href="{{ file }}" type="text/css" />
{% endfor %}
{% for css in css_raw %}
    <style>
        {{ css }}
    </style>
{% endfor %}
DOC_JS = <Template 'doc_js.js'>#
模板:doc_js.js
{% extends "try_run.js" %}

{% block code_to_run %}
  const docs_json = {{ docs_json }};
  const render_items = {{ render_items }};
  root.Bokeh.embed.embed_items(docs_json, render_items{%- if app_path -%}, "{{ app_path }}" {%- endif -%}{%- if absolute_url -%}, "{{ absolute_url }}" {%- endif -%});
{% endblock %}
DOC_NB_JS = <Template 'doc_nb_js.js'>#
模板:doc_nb_js.js
{% extends "try_run.js" %}

{% block code_to_run %}
  const docs_json = {{ docs_json }};
  const render_items = {{ render_items }};
  void root.Bokeh.embed.embed_items_notebook(docs_json, render_items);
{% endblock %}
FILE = <Template 'file.html'>#

将 Bokeh 模型渲染到基本的 .html 文件中。

参数:
  • title (str) – <title> 标签的值

  • plot_resources (str) – 通常是 RESOURCES 的输出

  • plot_script (str) – 通常是 PLOT_SCRIPT 的输出

用户可以通过提供接受这些相同参数的自己的 Jinja2 模板来自定义文件输出。

模板:file.html
{% from macros import embed %}
<!DOCTYPE html>
<html lang="en">
  {% block head %}
  <head>
  {% block inner_head %}
    <meta charset="utf-8">
    <title>{% block title %}{{ title | e if title else "Bokeh Plot" }}{% endblock %}</title>
  {%  block preamble -%}{%- endblock %}
  {%  block resources %}
    <style>
      html, body {
        box-sizing: border-box;
        display: flow-root;
        height: 100%;
        margin: 0;
        padding: 0;
      }
    </style>
  {%   block css_resources -%}
    {{- bokeh_css if bokeh_css }}
  {%-  endblock css_resources %}
  {%   block js_resources -%}
    {{  bokeh_js if bokeh_js }}
  {%-  endblock js_resources %}
  {%  endblock resources %}
  {%  block postamble %}{% endblock %}
  {% endblock inner_head %}
  </head>
  {% endblock head%}
  {% block body %}
  <body>
  {%  block inner_body %}
  {%    block contents %}
  {%      for doc in docs %}
  {{        embed(doc) if doc.elementid }}
  {%-       for root in doc.roots %}
  {%          block root scoped %}
  {{            embed(root) }}
  {%          endblock %}
  {%        endfor %}
  {%      endfor %}
  {%    endblock contents %}
  {{ plot_script | indent(4) }}
  {%  endblock inner_body %}
  </body>
  {% endblock body%}
</html>
JS_RESOURCES = <Template 'js_resources.html'>#

根据 Resources 对象中的配置,渲染加载 BokehJS JavaScript 代码和 CSS 的 HTML。

参数:
  • hashes (dict[str, str]) – 文件名到 SRI 哈希的映射

  • js_files (list[str]) – 要包含的 JS 文件的 URI 列表

  • js_raw (list[str]) – 要放在 <style> 标签之间的原始 JS 代码片段列表

模板:js_resources.html
{% for file in js_files %}
    <script type="text/javascript" src="{{ file }}"></script>
{% endfor %}
{% for js in js_raw %}
    <script type="text/javascript">
        {{ js }}
    </script>
{% endfor %}
NOTEBOOK_LOAD = <Template 'notebook_load.html'>#

根据 Resources 对象,渲染将 BokehJS JavaScript 代码和 CSS 加载到 Jupyter Notebook 中的 HTML。

参数:
  • plot_resources (str) – 通常是 RESOURCES 的输出

  • verbose (bool) – 是否显示有关 BokehJS 配置等的信息

  • bokeh_version (str) – Bokeh 的当前版本

  • js_info (str) – 关于 BokehJS 代码位置、版本等的详细信息

  • css_info (str) – 关于 BokehJS css 位置、版本等的详细信息

  • warnings (list[str]) – 要向用户显示的警告列表

模板:notebook_load.html
<style>
    .bk-notebook-logo {
        display: block;
        width: 20px;
        height: 20px;
        background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAABx0RVh0U29mdHdhcmUAQWRvYmUgRmlyZXdvcmtzIENTNui8sowAAAOkSURBVDiNjZRtaJVlGMd/1/08zzln5zjP1LWcU9N0NkN8m2CYjpgQYQXqSs0I84OLIC0hkEKoPtiH3gmKoiJDU7QpLgoLjLIQCpEsNJ1vqUOdO7ppbuec5+V+rj4ctwzd8IIbbi6u+8f1539dt3A78eXC7QizUF7gyV1fD1Yqg4JWz84yffhm0qkFqBogB9rM8tZdtwVsPUhWhGcFJngGeWrPzHm5oaMmkfEg1usvLFyc8jLRqDOMru7AyC8saQr7GG7f5fvDeH7Ej8CM66nIF+8yngt6HWaKh7k49Soy9nXurCi1o3qUbS3zWfrYeQDTB/Qj6kX6Ybhw4B+bOYoLKCC9H3Nu/leUTZ1JdRWkkn2ldcCamzrcf47KKXdAJllSlxAOkRgyHsGC/zRday5Qld9DyoM4/q/rUoy/CXh3jzOu3bHUVZeU+DEn8FInkPBFlu3+nW3Nw0mk6vCDiWg8CeJaxEwuHS3+z5RgY+YBR6V1Z1nxSOfoaPa4LASWxxdNp+VWTk7+4vzaou8v8PN+xo+KY2xsw6une2frhw05CTYOmQvsEhjhWjn0bmXPjpE1+kplmmkP3suftwTubK9Vq22qKmrBhpY4jvd5afdRA3wGjFAgcnTK2s4hY0/GPNIb0nErGMCRxWOOX64Z8RAC4oCXdklmEvcL8o0BfkNK4lUg9HTl+oPlQxdNo3Mg4Nv175e/1LDGzZen30MEjRUtmXSfiTVu1kK8W4txyV6BMKlbgk3lMwYCiusNy9fVfvvwMxv8Ynl6vxoByANLTWplvuj/nF9m2+PDtt1eiHPBr1oIfhCChQMBw6Aw0UulqTKZdfVvfG7VcfIqLG9bcldL/+pdWTLxLUy8Qq38heUIjh4XlzZxzQm19lLFlr8vdQ97rjZVOLf8nclzckbcD4wxXMidpX30sFd37Fv/GtwwhzhxGVAprjbg0gCAEeIgwCZyTV2Z1REEW8O4py0wsjeloKoMr6iCY6dP92H6Vw/oTyICIthibxjm/DfN9lVz8IqtqKYLUXfoKVMVQVVJOElGjrnnUt9T9wbgp8AyYKaGlqingHZU/uG2NTZSVqwHQTWkx9hxjkpWDaCg6Ckj5qebgBVbT3V3NNXMSiWSDdGV3hrtzla7J+duwPOToIg42ChPQOQjspnSlp1V+Gjdged7+8UN5CRAV7a5EdFNwCjEaBR27b3W890TE7g24NAP/mMDXRWrGoFPQI9ls/MWO2dWFAar/xcOIImbbpA3zgAAAABJRU5ErkJggg==);
    }
</style>
<div>
    <a href="https://bokeh.org.cn" target="_blank" class="bk-notebook-logo"></a>
    <span id="{{ element_id }}">Loading BokehJS ...</span>
</div>
{% if verbose %}
<style>
    p.bokeh_notebook { margin-left: 24px; }
    table.bokeh_notebook {
        border: 1px solid #e7e7e7;
        margin: 5px;
        margin-left: 24px;
        width: 80%;
    }
    tr.bokeh_notebook {
        border: 1px solid #e7e7e7;
        background-color: #FFF;
    }
    th.bokeh_notebook {
        border: 1px solid #e7e7e7;
        background-color: #f8f8f8;
        text-align: center;
    }
    td.bokeh_notebook {
        border: 1px solid #e7e7e7;
        background-color: #d2d7ec;
        text-align: left;
    }
</style>
<p class="bokeh_notebook">Using Settings:</p>
<table class="bokeh_notebook">
    <tr class="bokeh_notebook">
        <th class="bokeh_notebook">Bokeh</th>
        <th class="bokeh_notebook">version</th>
        <td class="bokeh_notebook">{{ bokeh_version }}</td>
    </tr>
    <tr class="bokeh_notebook">
        <th class="bokeh_notebook" rowspan="2">BokehJS</th>
        <th class="bokeh_notebook">js</th>
        <td class="bokeh_notebook">{{ js_info }}</td>
    </tr>
    <tr class="bokeh_notebook">
        <th class="bokeh_notebook">css</th>
        <td class="bokeh_notebook">{{ css_info }}</td>
    </tr>
</table>
{% endif %}
{% for warning in warnings %}
<p style="background-color: #f2d7dc;">{{ warning }}</p>
{% endfor %}
PLOT_DIV = <Template 'plot_div.html'>#

渲染一个基本的绘图 div,可与 PLOT_JS 结合使用。

参数:

elementid (str) – <div> 的唯一标识符,PLOT_JS 模板应配置为具有相同的 elementid

模板:plot_div.html
{% from macros import embed %}
{{ embed(doc) if doc.elementid }}
{% for root in doc.roots %}
  {{ embed(root) }}
{% endfor %}
ROOT_DIV = <Template 'root_div.html'>#
模板:root_div.html
{% from macros import embed %}
{{ embed(root) }}
SCRIPT_TAG = <Template 'script_tag.html'>#

渲染用于原始 JavaScript 代码的 <script> 标签。

参数:

js_code (str) – 要放在标签中的原始 JavaScript 代码。

模板:script_tag.html
<script type="{{ type }}"{% if id %} id="{{ id }}"{% endif %}>

{{ js_code }}
</script>