如何使用“重置”回调按钮将bokeh图形重置为初始状态?

时间:2020-04-24 03:23:36

标签: python plot server bokeh line-plot

我一直在使用bokeh可视化来显示基于代理模型(ABM)模拟的结果的项目。在recent post中,我得到了帮助,可以在非常简化的模拟版本中正确传输数据。我的下一个任务很容易理解,就是在布局中添加一个“重置”按钮,这样我就可以将图形恢复到其初始状态,并再次从“步骤0”开始运行仿真。令人惊讶的是,似乎没有简单的方法可以做到这一点。我尝试了几种不同的方法,包括重新初始化我的所有数据和重新填充ColumnDataSources,但是我无法使以前运行模拟中的数据消失。这是一个说明问题的独立代码示例:

import colorcet as cc
from bokeh.server.server import Server
from bokeh.application import Application
from bokeh.application.handlers.function import FunctionHandler
from bokeh.plotting import figure, ColumnDataSource
from bokeh.models import Button
from bokeh.layouts import column
import random

def make_document(doc):

    # make a list of groups
    strategies = ['DD', 'DC', 'CD', 'CCDD']

    # initialize some vars
    step = 0
    callback_obj = None  
    colors = cc.glasbey_dark
    #num_colors = len(colors)
    # create a list to hold all CDSs for active strategies in next step
    sources = []

    # Create a figure container
    fig = figure(title='Streaming Line Plot - Step 0', plot_width=1400, plot_height=400)

    # get step 0 data for initial strategies
    for i in range(len(strategies)):
        step_data = dict(step=[step], 
                        strategy = [strategies[i]],
                        ncount=[random.choice(range(1, 100))])
        data_source = ColumnDataSource(step_data)
        color = colors[i]
        # this will create one fig.line renderer for each strategy & its data for this step
        fig.line(x='step', y='ncount', source=data_source, color=color, line_width=2)
        # add this CDS to the sources list
        sources.append(data_source)

    def button1_run():
        nonlocal callback_obj
        if button1.label == 'Run':
            button1.label = 'Stop'
            button1.button_type='danger'
            callback_obj = doc.add_periodic_callback(button2_step, 100)
        else:
            button1.label = 'Run'
            button1.button_type = 'success'
            doc.remove_periodic_callback(callback_obj)

    def button2_step():
        nonlocal step
        data = []
        step += 1
        fig.title.text = 'Streaming Line Plot - Step '+str(step)
        for i in range(len(strategies)):
            step_data = dict(step=[step], 
                            strategy = [strategies[i]],
                            ncount=[random.choice(range(1, 100))])
            data.append(step_data)
        for source, data in zip(sources, data):
            source.stream(data)        

    def button3_reset():
        step = 0
        fig.title.text = 'Streaming Line Plot - Step '+str(step)

        for i in range(len(strategies)):
            init_data = dict(step=[step], 
                            strategy = [strategies[i]],
                            ncount=[random.choice(range(1, 100))])
            reset_source = ColumnDataSource(init_data)
            print(init_data)
            color = colors[i]
            # this will create one fig.line renderer for each strategy & its data for this step
            fig.line(x='step', y='ncount', source=reset_source, color=color, line_width=2)
            # add this CDS to the sources list
            sources.append(reset_source)


    # add on_click callback for button widget
    button1 = Button(label="Run", button_type='success', width=390)
    button1.on_click(button1_run)
    button2 = Button(label="Step", button_type='primary', width=390)
    button2.on_click(button2_step)
    button3 = Button(label="Reset", button_type='warning', width=390)
    button3.on_click(button3_reset)

    doc.add_root(column(fig, button1, button2, button3))
    doc.title = "Now with live updating!"

apps = {'/': Application(FunctionHandler(make_document))}

server = Server(apps, port=5004)
server.start()

if __name__ == '__main__':
    server.io_loop.add_callback(server.show, "/")
    server.io_loop.start()

我要在button3_reset代码中尝试做的基本上是在make_document函数顶部重复初始化。但是,即使该代码工作相同(从button3步骤的中间出现的打印输出中可以看出),我也无法使图形重置为初始的空状态。我已经阅读了许多堆栈溢出文章和其他bokeh文档,但没有找到我认为简单的问题的简单答案:如何将bokeh线图重置为其原始状态,以便可以运行数据流又从起点开始了?

我正在使用bokeh 1.4.0(anaconda不允许我更新),python 3.7.6,spyder 4.0.1以及用于可视化的Chrome和Brave浏览器。

2 个答案:

答案 0 :(得分:0)

您的button3_reset代码不会清除任何内容-只会在现有内容之上添加新内容。

相反,您应该仅遍历sources列表,并将每个源的data属性设置为代码中第一个循环中使用的初始值。这意味着,您还必须将这些数据保存在某个地方。

答案 1 :(得分:0)

通常,我使事情变得非常复杂。这是完成任务的button3_reset的代码。

 def button3_reset():
        nonlocal step
        step = 0
        data = []
        fig.title.text = 'Streaming Line Plot - Step '+str(step)

        for i in range(len(strategies)):
            init_data = dict(step=[step], 
                            strategy = [strategies[i]],
                            ncount=[random.choice(range(1, 100))])
            data.append(init_data)

        for source, data in zip(sources, data):
            source.data = data

我之前所做的是生成新的CDS,但是旧的CDS仍然嵌入在图中。再次感谢Eugene的技巧,我意识到我只需要重新分配现有CDS的.data属性,而无需创建新CDS。然后,为整理起见,我不得不将标题更新回“ Step 0”,然后将step设为非局部变量,因此button2_step会知道从1开始重新编号。它应该做什么。再次感谢您的答复。