bokeh.py 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126
  1. import asyncio
  2. import re
  3. from collections.abc import Sequence
  4. from pywebio.output import *
  5. requirejs_tpl = """
  6. %s
  7. <script type="text/javascript">
  8. requirejs(['bokeh', 'bokeh-widgets', 'bokeh-tables', 'bokeh-gl'], function(Bokeh) {
  9. %s
  10. });
  11. </script>
  12. """
  13. def load_notebook(resources=None, verbose=False, hide_banner=False, load_timeout=5000):
  14. """加载 Bokeh 资源
  15. :param resources: 目前不支持自定义静态资源的链接
  16. :param verbose: 开启 Bokeh 日志 并显示 Bokeh 加载标签
  17. :param hide_banner: 不支持
  18. :param load_timeout: 不支持
  19. :return: None
  20. """
  21. from bokeh.util.serialization import make_id
  22. js_gists = ["console.log('Load BokehJS complete.')"]
  23. html = ''
  24. if verbose:
  25. element_id = make_id()
  26. html += """
  27. <div class="bk-root">
  28. <a href="https://bokeh.org" target="_blank" class="bk-logo bk-logo-small bk-logo-notebook"></a>
  29. <span id="{element_id}" style="font-family: Helvetica, Arial, sans-serif;font-size: 13px;">Loading BokehJS ...</span>
  30. </div>
  31. """.format(element_id=element_id)
  32. js_gists.append(
  33. "document.getElementById({element_id}).innerHTML = 'Load BokehJS complete.'".format(element_id=element_id))
  34. js_gists.append('Bokeh.set_log_level("info");')
  35. js_gists.append("console.log('Set bokeh log level to INFO because you set `output_notebook(verbose=True)`')")
  36. put_html(requirejs_tpl % (html, '\n'.join(js_gists)))
  37. def show_doc(obj, state, notebook_handle):
  38. """显示 Bokeh 单个 documents
  39. :param obj:
  40. :param state:
  41. :param notebook_handle: 不支持
  42. :return:
  43. """
  44. from bokeh.embed import components
  45. script, div = components(obj, wrap_script=False)
  46. if isinstance(obj, Sequence):
  47. div = '\n'.join(div)
  48. elif isinstance(obj, dict):
  49. div = '\n'.join(div[k] for k in obj.keys())
  50. put_html(requirejs_tpl % (div, script))
  51. def show_app(app, state, notebook_url, port=0, **kw):
  52. """显示 Bokeh applications
  53. :param app: A Bokeh Application to embed in PyWebIO.
  54. :param state: ** Unused **
  55. :param notebook_url: PyWebIO server 的地址,用于设置 Bokeh Server origin白名单
  56. :param port: Bokeh Server 端口
  57. :param kw: 传给 Bokeh Server 的额外参数
  58. """
  59. from bokeh.server.server import Server
  60. from bokeh.io.notebook import _origin_url, uuid4, curstate, _server_url
  61. from pywebio.platform.tornado import ioloop
  62. loop = ioloop()
  63. loop.make_current()
  64. asyncio.set_event_loop(loop.asyncio_loop)
  65. # loop = IOLoop.current()
  66. if callable(notebook_url):
  67. origin = notebook_url(None)
  68. else:
  69. origin = _origin_url(notebook_url)
  70. server = Server({"/": app}, io_loop=loop, port=port, allow_websocket_origin=[origin], **kw)
  71. server_id = uuid4().hex
  72. curstate().uuid_to_server[server_id] = server
  73. server.start()
  74. if callable(notebook_url):
  75. url = notebook_url(server.port)
  76. else:
  77. url = _server_url(notebook_url, server.port)
  78. from bokeh.embed import server_document
  79. script = server_document(url, resources=None)
  80. script = re.sub(r'<script(.*?)>([\s\S]*?)</script>', r"""
  81. <script \g<1>>
  82. requirejs(['bokeh', 'bokeh-widgets', 'bokeh-tables', 'bokeh-gl'], function(Bokeh) {
  83. \g<2>
  84. });
  85. </script>
  86. """, script)
  87. put_html(script)
  88. def try_install_bokeh_hook():
  89. """尝试安装bokeh支持"""
  90. try:
  91. from bokeh.io import install_notebook_hook
  92. except ImportError:
  93. return False
  94. install_notebook_hook('pywebio', load_notebook, show_doc, show_app)
  95. return True