【问题标题】:How to embed a DataTable widget in a Python Flask Web App如何在 Python Flask Web App 中嵌入 DataTable 小部件
【发布时间】:2019-03-22 13:49:30
【问题描述】:

如果这个问题在 StackOverflow 上的其他地方得到解答,我深表歉意,在发布这个问题之前,我已尽力搜索答案。

我正在努力使用 Bokeh 创建一个基于 Flask 的 Web 应用程序,以便在工作中进行数据可视化。使用 bokeh.embed.components 和 bokeh.plotting.figure 嵌入散点图/线图没有问题。

我想在绘图下方的 DataTable 小部件中显示用于制作绘图的数据。不幸的是,使用组件生成脚本和 div 似乎不适用于小部件。使用通过 bokeh.layouts 创建的列、行或布局时,组件似乎也会失败。

我的代码的总体布局是一个 Python 文件,其中包含我的 Flask 应用程序,以及一个包含我的网页布局的 HTML 文件。 Python文件的大体布局如下:

from flask import Flask, render_template
import pandas as pd
import numpy as np
from bokeh.models import ColumnDataSource
from bokeh.models.widgets import DataTable, TableColumn
from bokeh.plotting import figure
from bokeh.layouts import widgetbox, column
from bokeh.embed import components

app = Flask(__name__)
x = np.linspace(0, 2, 1000)
y = np.sin(x)
df = pd.DataFrame({"x": x, "y":y}) # Not sure how to provide sample data

@app.route("/")
def index():
    p = figure()
    p.scatter(df['x'], df['y'])

    data_source = ColumnDataSource(df)
    columns = [
        TableColumn(field="field1", title="Field 1"),
        TableColumn(field="field2", title="Field 2"),
        TableColumn(field="field3", title="Field 3"),
    ]

    data_table = DataTable(source=data_source, columns=columns)
    script, div = components(column(p, widgetbox(data_table)))
    return render_template('sample.html', script=script, div=div)

HTML 模板(“sample.html”)如下所示:

<html>
<head>
<link
    href="http://cdn.bokeh.org/bokeh/release/bokeh-1.0.4.min.css"
    rel="stylesheet" type="text/css">
<link
    href="http://cdn.bokeh.org/bokeh/release/bokeh-widgets-1.0.4.min.css"
    rel="stylesheet" type="text/css">

<script src="http://cdn.bokeh.org/bokeh/release/bokeh-1.0.4.min.js"></script>
<script src="http://cdn.bokeh.org/bokeh/release/bokeh-widgets-1.0.4.min.js"></script>

</head>
<body>
{{ script|safe }}
{{ div|safe }}
</body>
</html>

我选择使用简单的正弦波作为此代码的示例数据,但在现实生活中,我使用 Excel 文件 (pd.read_excel) 在将 Web 应用程序连接到数据库之前测试代码。

在 Python 代码中,如果我替换

script, div = components(column(p, widgetbox(data_table))

script, div = components(p)

代码完美运行。所以,这让我相信问题在于嵌入小部件或绘图和小部件的布局。提前感谢您提供的任何帮助。

【问题讨论】:

  • 没有minimal reproducible example,我不清楚任何类型的答案应该是什么样子
  • 我可以尝试提供这样一个例子。我应该发一个新帖子还是只在这个评论部分提供?
  • “保留发布工作代码” - 我怀疑源的内容是否足以揭示代码的敏感机制。您是否能够专注于单行代码或有问题的功能块?我确信发布这些内容中的任何一个都不会泄露代码的复杂性。
  • 两者都不是。您可以edit您的问题提供minimal reproducible example
  • 好的,我目前正在编辑,应该很快就会完成。对不起那些犯错的人

标签: python flask bokeh


【解决方案1】:

我意识到我的原始代码出了什么问题。显然,在将 DataTables 与组件一起使用时,需要链接到散景表的 CSS 和 JS。下面是相关的链接和脚本标签。

<link
    href="http://cdn.bokeh.org/bokeh/release/bokeh-tables-1.0.4.min.css"
    rel="stylesheet" type="text/css">

<script src="http://cdn.bokeh.org/bokeh/release/bokeh-tables-1.0.4.min.js"></script>

【讨论】:

  • 手动添加资源是一种不好的做法(因此您在这里遇到问题的原因是;)。将所有必需的资源自动添加到您的模板中确实容易得多。改为使用 from bokeh.resources import INLINE 然后 render_template(resources = INLINE.render()) 并在 HTML 中添加 {{ resources }}模板。
  • 托尼,感谢您的建议。我注释掉了我手动添加的所有资源,并通过 INLINE.render() 调用遵循了您的建议。它工作得很好,只是我需要将 {{ resources|safe }} 添加到我的 HTML 模板而不是 {{ resources }}。我以前必须用 {{ script|safe }} 和 {{ div|safe }} 来做这件事。我知道这是题外话,但你能解释一下这个安全关键字的功能吗?
  • safe 过滤器指示模板引擎跳过传递给模板引擎的文本中的特殊字符的转义(在本例中为 resources)。显然,散景 INLINE.render() 返回一个带有特殊字符的文本。
【解决方案2】:

下面的代码为 Bokeh v1.0.4 正确渲染了绘图和表格

import numpy as np
import pandas as pd
import webbrowser
from flask import Flask, render_template
from tornado.ioloop import IOLoop
from bokeh.application import Application
from bokeh.application.handlers import FunctionHandler
from bokeh.embed import server_document
from bokeh.models import ColumnDataSource
from bokeh.plotting import figure
from bokeh.models.widgets import DataTable, TableColumn
from bokeh.server.server import Server
from bokeh.layouts import column

app = Flask(__name__)
port = 5001

def get_plot(doc):
    x = np.linspace(0, 2, 1000)
    y = np.sin(x)
    df = pd.DataFrame({"x": x, "y":y})  # Not sure how to provide sample data

    def get_plot():
        p = figure()
        p.scatter(df['x'], df['y'])
        data_source = ColumnDataSource(df)
        columns = [ TableColumn(field = "field1", title = "Field 1"),
                    TableColumn(field = "field2", title = "Field 2"),
                    TableColumn(field = "field3", title = "Field 3"), ]
        data_table = DataTable(source = data_source, columns = columns)

        return column(p, data_table)

    doc.add_root(get_plot())
    doc.title = "Flask App Plot"

bokeh_app = Application(FunctionHandler(get_plot))

@app.route('/', methods = ['GET'])
def index():
    script = server_document('http://localhost:5006/bkapp')
    return render_template("index.html", script = script)

def bk_worker():
    server = Server({'/bkapp': bokeh_app}, io_loop = IOLoop(), allow_websocket_origin = ["localhost:{}".format(port)], port = port)
    server.start()
    server.io_loop.start()

from threading import Thread
Thread(target = bk_worker).start()

if __name__ == '__main__':
    print('Opening single process Flask app with embedded Bokeh application on http://localhost:{}/'.format(port))
    webbrowser.open_new("http://localhost:{}/".format(port))
    app.run(port = port, debug = False)

结果:

【讨论】:

  • 感谢您对托尼的出色回应。这确实起到了作用,而且比我原来的评论更先进/更有效。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-09-14
  • 2017-01-17
  • 2021-10-03
  • 2018-10-22
  • 2013-01-22
  • 2023-01-31
  • 1970-01-01
相关资源
最近更新 更多