ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

python – 如何将Bokeh与Angular 4集成?

2019-10-09 05:11:08  阅读:246  来源: 互联网

标签:python angular bokeh


我在后端生成了散景图,我想将其嵌入到已经存在的角度4 webapp中.
目前我使用的是bokeh.components函数,它产生以下两个html标签.

<script type="text/javascript">
  (function() {
    var fn = function() {
      Bokeh.safely(function() {
        (function(root) {
          function embed_document(root) {

          var docs_json = '{"d199dc51-c663-4220-800f-a5f0d7c3a97a":{"roots":{"references":[{"attributes":{"bottom_units":"screen","fill_alpha":{"value":0.5},"fill_color":{"value":"lightgrey"},"left_units":"screen","level":"overlay","line_alpha":{"value":1.0},"line_color":{"value":"black"},"line_dash":[4,4],"line_width":{"value":2},"plot":null,"render_mode":"css","right_units":"screen","top_units":"screen"},"id":"f795b234-3b1e-4117-aa31-bccbbaaccf71","type":"BoxAnnotation"},{"attributes":{"axis_line_color":{"value":null},"formatter":{"id":"8c794376-5b4c-42f6-bcf4-662922d47df1","type":"BasicTickFormatter"},"major_tick_line_color":{"value":null},"minor_tick_line_color":{"value":null},"plot":{"id":"6fbff9b0-a4fc-4fc1-a50e-d34f906928b2","subtype":"Figure","type":"Plot"},"ticker":{"id":"5468ade8-3979-45b4-9dd0-fbb3cab5b888","type":"BasicTicker"},"visible":null},"id":"be01b966-482b-4e6f-8c6f-8bc0373791aa","type":"LinearAxis"},{"attributes":{},"id":"f9e57a1a-b5e1-4c3f-8e0d-4789dca393ec","type":"SaveTool"},{"attributes":{"fill_color":{"value":"#6C69EB"},"height":{"value":0.5},"right":{"field":"Sales"},"y":{"field":"ID"}},"id":"4dc67234-e29e-4e53-997c-3a6b2e40d5e5","type":"HBar"},{"attributes":{},"id":"db86af55-1185-4097-839a-8d5dc01f19a2","type":"LinearScale"},{"attributes":{},"id":"538fede7-868b-4740-a75e-cb6065ac1194","type":"ResetTool"},{"attributes":{"source":{"id":"946bc6d9-32c8-4dfe-8d34-a29c7c06a242","type":"ColumnDataSource"}},"id":"4ff758a6-c013-4e17-ae69-5c039b0c9394","type":"CDSView"},{"attributes":{"callback":null,"factors":["1348","1226","1306","1268","1103","1129","1340","1470","1783","1346","1264","1387","1775","1247","1771","1770","1773","1772","1190","1733","1727","1694","1584","1503","1113","404","403","401","509","1119","1653","1467","1677","1670","1565","1391","1699","995","1235","1314","1759","1218","1336","1553","1668","1134","1493","1742","1561","1501","1567","1676","1678","1251","1664","1056","1557","1753","1602","1288","1438","1657","1681","1604","1768","1105","1750","1009","1361","1666","1259","1766","1382"]},"id":"2949c368-f95d-4a48-addb-988e5d963ff9","type":"FactorRange"},{"attributes":{},"id":"925e5c0f-b284-4614-a702-33c0ee4bcf34","type":"WheelZoomTool"},{"attributes":{"grid_line_color":{"value":null},"plot":{"id":"6fbff9b0-a4fc-4fc1-a50e-d34f906928b2","subtype":"Figure","type":"Plot"},"ticker":{"id":"5468ade8-3979-45b4-9dd0-fbb3cab5b888","type":"BasicTicker"}},"id":"b1bd251f-e88f-46f1-ac91-5ab1d74ece4b","type":"Grid"},{"attributes":{},"id":"4f69bbc9-c6df-4c64-976c-9013fddff8db","type":"CategoricalTickFormatter"},{"attributes":{"callback":null,"tooltips":"\\n    &lt;div&gt;\\n        &lt;div&gt;\\n            &lt;span style=\\"font-size: 10px;\\"&gt;Merch_Ifonafed:&lt;/span&gt;\\n            &lt;span style=\\"font-size: 15px; font-weight: bold;\\"&gt;@ID&lt;/span&gt;\\n        &lt;div&gt;\\n        \\n        &lt;div&gt;\\n            &lt;span style=\\"font-size: 10px;\\"&gt;Revenue:&lt;/span&gt;\\n            &lt;span style= font-size:15px; font-weight: bold;&gt;&amp;#x20B9;&lt;/span&gt; \\n            &lt;span style=\\"font-size: 15px; font-weight: bold;\\"&gt;@Sales{0,0.000}&lt;/span&gt;\\n        &lt;div&gt;\\n        \\n        &lt;div&gt;\\n            &lt;span style=\\"font-size: 10px;\\"&gt;Visits:&lt;/span&gt;\\n            &lt;span style=\\"font-size: 15px; font-weight: bold;\\"&gt;@Count&lt;/span&gt;            \\n        &lt;div&gt;\\n        \\n        &lt;div&gt;\\n            &lt;span style=\\"font-size: 10px;\\"&gt;Avg Val/Tr:&lt;/span&gt;\\n            &lt;span style= font-size:15px; font-weight: bold;&gt;&amp;#x20B9;&lt;/span&gt; \\n            &lt;span style=\\"font-size: 15px; font-weight: bold;\\"&gt;@AVG&lt;/span&gt;\\n\\n            \\n        &lt;div&gt;\\n        \\n        &lt;div&gt;\\n            &lt;span style=\\"font-size: 10px;\\"&gt;Mode:&lt;/span&gt;\\n            &lt;span style= font-size:15px; font-weight: bold;&gt;&amp;#x20B9;&lt;/span&gt; \\n            &lt;span style=\\"font-size: 15px; font-weight: bold;\\"&gt;@Mode&lt;/span&gt;\\n\\n        &lt;div&gt;\\n       \\n        &lt;div&gt;\\n            &lt;span style=\\"font-size: 10px;\\"&gt;Unique_Visitors:&lt;/span&gt;\\n            &lt;span style=\\"font-size: 15px; font-weight: bold;\\"&gt;@Unique_guests&lt;/span&gt;\\n\\n        &lt;/div&gt;\\n    &lt;/div&gt;\\n    "},"id":"508c69b2-507e-4091-8056-05e75b6a53bf","type":"HoverTool"},{"attributes":{},"id":"af2d3da9-5a8e-48f8-acb3-43d7d7778809","type":"HelpTool"},{"attributes":{},"id":"3de664c0-c02a-4eda-9c07-7e580557f395","type":"CategoricalScale"},{"attributes":{"axis_line_color":{"value":null},"formatter":{"id":"4f69bbc9-c6df-4c64-976c-9013fddff8db","type":"CategoricalTickFormatter"},"plot":{"id":"6fbff9b0-a4fc-4fc1-a50e-d34f906928b2","subtype":"Figure","type":"Plot"},"ticker":{"id":"89f1fa5c-f135-47df-8383-cb589af6606f","type":"CategoricalTicker"}},"id":"e9113819-fe22-4051-bbaa-c568873d85fe","type":"CategoricalAxis"},{"attributes":{"data_source":{"id":"946bc6d9-32c8-4dfe-8d34-a29c7c06a242","type":"ColumnDataSource"},"glyph":{"id":"4dc67234-e29e-4e53-997c-3a6b2e40d5e5","type":"HBar"},"hover_glyph":null,"muted_glyph":null,"view":{"id":"4ff758a6-c013-4e17-ae69-5c039b0c9394","type":"CDSView"}},"id":"9fed1d26-a3c2-4096-8c12-cca8bc423562","type":"GlyphRenderer"},{"attributes":{"below":[{"id":"be01b966-482b-4e6f-8c6f-8bc0373791aa","type":"LinearAxis"}],"left":[{"id":"e9113819-fe22-4051-bbaa-c568873d85fe","type":"CategoricalAxis"}],"outline_line_alpha":{"value":0.2},"renderers":[{"id":"be01b966-482b-4e6f-8c6f-8bc0373791aa","type":"LinearAxis"},{"id":"b1bd251f-e88f-46f1-ac91-5ab1d74ece4b","type":"Grid"},{"id":"e9113819-fe22-4051-bbaa-c568873d85fe","type":"CategoricalAxis"},{"id":"a48580a8-8254-4b66-a45c-a3e41210d5f4","type":"Grid"},{"id":"f795b234-3b1e-4117-aa31-bccbbaaccf71","type":"BoxAnnotation"},{"id":"9fed1d26-a3c2-4096-8c12-cca8bc423562","type":"GlyphRenderer"}],"title":{"id":"c23880dd-5727-4f94-9e1d-f1ecf3d2f71e","type":"Title"},"toolbar":{"id":"f380e570-a3bb-4430-9bb8-ba214afcf178","type":"Toolbar"},"x_range":{"id":"55df4eae-ccbb-4c6d-afa3-653995f8bb79","type":"DataRange1d"},"x_scale":{"id":"db86af55-1185-4097-839a-8d5dc01f19a2","type":"LinearScale"},"y_range":{"id":"2949c368-f95d-4a48-addb-988e5d963ff9","type":"FactorRange"},"y_scale":{"id":"3de664c0-c02a-4eda-9c07-7e580557f395","type":"CategoricalScale"}},"id":"6fbff9b0-a4fc-4fc1-a50e-d34f906928b2","subtype":"Figure","type":"Plot"},{"attributes":{},"id":"5468ade8-3979-45b4-9dd0-fbb3cab5b888","type":"BasicTicker"},{"attributes":{"plot":null,"text":""},"id":"c23880dd-5727-4f94-9e1d-f1ecf3d2f71e","type":"Title"},{"attributes":{"active_drag":"auto","active_inspect":"auto","active_scroll":"auto","active_tap":"auto","tools":[{"id":"883319d0-a94d-44bd-ad9d-8e7b4a393771","type":"PanTool"},{"id":"925e5c0f-b284-4614-a702-33c0ee4bcf34","type":"WheelZoomTool"},{"id":"ac6748d3-f7ae-4232-8ed3-74432a8f67ab","type":"BoxZoomTool"},{"id":"f9e57a1a-b5e1-4c3f-8e0d-4789dca393ec","type":"SaveTool"},{"id":"538fede7-868b-4740-a75e-cb6065ac1194","type":"ResetTool"},{"id":"af2d3da9-5a8e-48f8-acb3-43d7d7778809","type":"HelpTool"},{"id":"508c69b2-507e-4091-8056-05e75b6a53bf","type":"HoverTool"}]},"id":"f380e570-a3bb-4430-9bb8-ba214afcf178","type":"Toolbar"},{"attributes":{"overlay":{"id":"f795b234-3b1e-4117-aa31-bccbbaaccf71","type":"BoxAnnotation"}},"id":"ac6748d3-f7ae-4232-8ed3-74432a8f67ab","type":"BoxZoomTool"},{"attributes":{"callback":null,"column_names":["Count","AVG","ID","Sales"],"data":{"AVG":[null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null],"Count":[11,473,224,12,2522,1,3,1668,4,116,355,248,7,9,2,4,1922,1,1613,62,239,1,135,14,685,303,720,1092,1,264,1384,311,3,1680,903,80,2,337,249,207,1,271,2,1,440,1312,74,1169,3,427,1303,36,46,1002,97,438,2,2,272,1287,1205,212,6,1142,42,925,3,1130,13,692,887,166,446],"ID":["1348","1226","1306","1268","1103","1129","1340","1470","1783","1346","1264","1387","1775","1247","1771","1770","1773","1772","1190","1733","1727","1694","1584","1503","1113","404","403","401","509","1119","1653","1467","1677","1670","1565","1391","1699","995","1235","1314","1759","1218","1336","1553","1668","1134","1493","1742","1561","1501","1567","1676","1678","1251","1664","1056","1557","1753","1602","1288","1438","1657","1681","1604","1768","1105","1750","1009","1361","1666","1259","1766","1382"],"Sales":[14920,113230,50641,2400,585233,0,0,438801,900,56428,116718,89224,0,0,780,0,552609,0,389764,14688,50450,0,28240,2680,147458,89125,183097,266313,0,2094625,499393,90876,10,434509,238438,17970,290,141866,85690,47865,0,71074,320,0,89103,348101,20536,283752,0,138798,341643,8950,8451,335189,23034,125857,0,0,80885,342261,351727,52722,732,324171,11451,245051,0,370611,2310,161049,257464,41871,107453]}},"id":"946bc6d9-32c8-4dfe-8d34-a29c7c06a242","type":"ColumnDataSource"},{"attributes":{"dimension":1,"grid_line_color":{"value":null},"plot":{"id":"6fbff9b0-a4fc-4fc1-a50e-d34f906928b2","subtype":"Figure","type":"Plot"},"ticker":{"id":"89f1fa5c-f135-47df-8383-cb589af6606f","type":"CategoricalTicker"}},"id":"a48580a8-8254-4b66-a45c-a3e41210d5f4","type":"Grid"},{"attributes":{},"id":"883319d0-a94d-44bd-ad9d-8e7b4a393771","type":"PanTool"},{"attributes":{},"id":"89f1fa5c-f135-47df-8383-cb589af6606f","type":"CategoricalTicker"},{"attributes":{"callback":null},"id":"55df4eae-ccbb-4c6d-afa3-653995f8bb79","type":"DataRange1d"},{"attributes":{},"id":"8c794376-5b4c-42f6-bcf4-662922d47df1","type":"BasicTickFormatter"}],"root_ids":["6fbff9b0-a4fc-4fc1-a50e-d34f906928b2"]},"title":"Bokeh Application","version":"0.12.13"}}';
          var render_items = [{"docid":"d199dc51-c663-4220-800f-a5f0d7c3a97a","elementid":"74705daf-2fe7-47e6-9800-0ecaf72e9708","modelid":"6fbff9b0-a4fc-4fc1-a50e-d34f906928b2"}];
          root.Bokeh.embed.embed_items(docs_json, render_items);

          }
          if (root.Bokeh !== undefined) {
            embed_document(root);
          } else {
            var attempts = 0;
            var timer = setInterval(function(root) {
              if (root.Bokeh !== undefined) {
                embed_document(root);
                clearInterval(timer);
              }
              attempts++;
              if (attempts > 100) {
                console.log("Bokeh: ERROR: Unable to run BokehJS code because BokehJS library is missing")
                clearInterval(timer);
              }
            }, 10, root)
          }
        })(window);
      });
    };
    if (document.readyState != "loading") fn();
    else document.addEventListener("DOMContentLoaded", fn);
  })();
</script>

以及

<div class="bk-root">
    <div class="bk-plotdiv" id="74705daf-2fe7-47e6-9800-0ecaf72e9708"></div>
</div>

我可以嵌入一个小样板:

<!DOCTYPE html>
    <html lang="en">
        <head>
            <meta charset="utf-8">
            <title>Bokeh Scatter Plots</title>

            <link rel="stylesheet" href="http://cdn.pydata.org/bokeh/release/bokeh-0.12.6.min.css" type="text/css" />
            <script type="text/javascript" src="http://cdn.pydata.org/bokeh/release/bokeh-0.12.6.min.js"></script>

            <script type="text/javascript">
  (function() {
    var fn = function() {
      Bokeh.safely(function() {
        (function(root) {
          function embed_document(root) {

          var docs_json = <!---docs_json---!>
          var render_item = <!---items---!>

        </head>
        <body>
            <div class="bk-root">
        <div class="bk-plotdiv" id=<!--- div_id ---!>></div>
    </div>
        </body>
    </html>

为此,我需要从前两个脚本中解析出重要的变量(docs_json,render_id,div_id),然后使用Flask将它们作为json发送到前端.很像这个google forms question

https://groups.google.com/a/continuum.io/forum/#!topic/bokeh/McLDXP414ps

Is there a better way to send my bokeh plot to the angular front end, instead of having to go through all of this ?

解决方法:

你希望能够从python后端发送更新事件,并拥有像Angular一样的按钮按下和文本输入事件等所有良好的交互,对吗?

我为你准备了一个漂亮而干净的角度项目,请参阅https://github.com/NuCOS/angular-bokeh,但你必须按照自己的意愿实现相互作用,我不想过度浮动这个实例与实际上没有直接连接的东西.你描述的问题.

简而言之,我表明你必须做更多的工作,你不能像在你的例子中那样将样板放入html部分.

为了将BokehJS lib集成到Angular项目中,我们使用名为bokeh-chart的角度组件.绘图数据很可能由websocket服务提供,在我们的示例中,我们使用aiohttp,但您可以自由设置任何其他websocket连接.
角度组件可以通过其标签名称集成到html中的任何位置,以下代码段显示了散景图表组件

<bokeh-chart></bokeh-chart>

散景图组件是一个常规的角度组件,带有html部分

<div [id]="id"></div>

和打字稿部分.图表组件只需要为自己的html部分提供id.图表的数据由在ngOnInit中组件初始化时调用的服务提供. Bokeh图表组件的相关打字稿部分如下所示:

...
export class BokehChartComponent implements OnInit {
  public id: string;

  constructor(
    private bokehService: BokehService) { }


 ngOnInit() {
     this.id = "chart";
     this.bokehService.getChart(this.id);
 }
}

由于BokehJS lib没有可用的类型,因此角度的集成并不像应该的那样简单.只能通过lib的全局公开对象访问该lib,在这种情况下,它也被称为Bokeh,它是嵌入图表所必需的唯一钩子.

// this is the global hook to the bokehjs lib (without types)
declare var Bokeh: any;

如果您将通常的java脚本插入到角度应用程序index.html的最顶层的html文件中,这种魔法只能按预期工作

<head>
 ...
  <link
    href="https://cdn.pydata.org/bokeh/release/bokeh-1.0.4.min.css"
    rel="stylesheet" type="text/css">
  <script src="https://cdn.pydata.org/bokeh/release/bokeh-1.0.4.min.js"></script>
 </head>

BokehService通过MessageService为图表提供数据,MessageService封装了与后端的连接,因此只需要通过公开的方法sendMsg(msg:Message)发送正确的消息.

export class BokehService extends Connector {
constructor(private msgService: MessageService) {
 super('BokehService');
 this.msgService.register(this);
 }
…
public getChart(id: string) {
 const msg = {
 name: 'addChart',
 args: [id],
 action: 'default'
 };
 this.msgService.sendMsg(msg);
 }

此服务还向后端公开了一个方法,该方法实际上将图表绘制到本机DOM元素中,我们必须首先删除先前的图

public plot(msg: Message) {
      const id = msg.args.id;
      const el = document.getElementById(id);
      // first remove the previous charts as child
      // this is necessary, since bokeh do not let us update a chart
      while (el.hasChildNodes()) {
            el.removeChild(el.lastChild);
      }
      // be sure to include the correct dom-id as second argument
      Bokeh.embed.embed_item(msg.args.item, id);
    }

我们示例中的后端服务是用python编写的.正如我所说,我们使用aiohttp作为Web服务器的异步解决方案.在浏览器中启动角度应用程序后,角度WebsocketService立即连接到服务器端的python后端.请记住,在生产中,您将在该点上实现更高的安全性,例如身份验证.后端已准备好从角度接收事件,例如给我Bokeh图表的数据.
addChart由来自angular的消息调用,将chartItem作为连接到websocket服务的json项发送

async def addChart(self, id_, user):
    """
    Example for adding a bokeh chart from backend

    """
    chartItem = self.chartProvider.chartExample()
    print("try to add chart for dom-id %s" % id_)
    context = {"name": "BokehService",
               "args": {"item": chartItem, "id": id_},
               "action": "plot"}
    await self.send_event(json.dumps(context), user=user)

这里有趣的部分是send_event方法,它实际上是基于我们对websocket服务器的实现.如前所述,该部分可能在您的个人实施方面有所不同.
图表的最小示例,也写为ChartProvider类的成员函数,看起来非常简单,只是为Bokeh中的普通sin-plot生成数据

import time
import numpy as np
from bokeh.plotting import figure
from bokeh.embed import json_item

class ChartProvider():
    def chartExample(self):
        t0 = time.time()
        # prepare some data
        self.phi += 0.02
        x = np.arange(0., 10., 0.1)
        y = np.sin(x + self.phi)
        # create a new plot
        p = figure()
        p.line(x, y, legend="SIN")
        chart_item = json_item(p)
        print(time.time()-t0)
        return chart_item

标签:python,angular,bokeh
来源: https://codeday.me/bug/20191009/1876843.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有