如何将Bokeh中的滑块值传递回Python代码

时间:2019-03-26 17:22:16

标签: python callback bokeh

我想将一个滑块值(我用Bokeh构建)传递回我的Python代码。该代码在绘图上生成2条线,并允许我更改其中一条的斜率和截距。但是,当我引入回调JavaScript并将滑块值作为"ff"传递回我的Python代码时,它失败了。
您能否通过回调语法帮助我,以将滑块值恢复为python(例如,参见代码的print(ff)最后一行)-我确实想做一些比最终打印出更有趣的事情!
来自回调的错误消息是:

  

ValueError:期望Dict(String,Instance(Model))的元素,得到{'my_dict':{'s':0.5}}

我的代码是:-

from ipywidgets import interact  
import numpy as np  
from bokeh.io import push_notebook, show, output_notebook  
from bokeh.plotting import figure  
from bokeh.models import ColumnDataSource  
from bokeh.models.callbacks import CustomJS  
output_notebook()  
x = np.linspace(0, 20, 200) # create equally spaced points.  
s = 0.5 # slope.  
i = 3 # intercept.  
y = s * x + i # straight line.  
my_dict = dict(s=s) # need to create a dict object to hold what gets passed in the callback.  

callback = CustomJS(args=dict(my_dict=my_dict), code="""  
  var ff = cb_obj.value  
  my_dict.change.emit()  
""")  
// ff should be the slider value.  

p = figure(title="simple line example", plot_height=300, plot_width=600, y_range=(-20,20),  
           background_fill_color='#efefef')  
r = p.line(x, y, color="#8888cc", line_width=1.5, alpha=0.8) # 1st line. This line can be controlled by sliders.  
q = p.line(x, 2*x+1.2, color="#0088cc", line_width=1.9, alpha=0.2) # 2nd line.  
def update(w=s, a=i):  
    r.data_source.data['y'] = w * x + a  # allow updates for the line r.  
    push_notebook()  
show(p, notebook_handle=True)  
interact(update, w=(-10,10), a=(-12,12) )  
print(ff)  # Return what the slider value is. I want ff accessible back in my python code.

2 个答案:

答案 0 :(得分:1)

这是解决方案。它创建一个bokeh服务器应用程序。通过使用名为20190328_start_bokeh_server.py的文件运行(从spyder) 有一条由滑块绘制和控制的直线。单击该按钮会将滑块值保存到一个csv文件中。

要获取以下代码以运行,请在控制台中使用以下代码(包含在20190404_start_bokeh_server.py中):

导入操作系统 os.chdir(“ C:\ Users”)#将工作目录更改为脚本位置。 os.system(“ start call bokeh serve --show 20190404_bokeh_server.py”)#或者:一旦我导航到包含.py文件的目录,就可以在anacondas提示符下键入此命令。 “”“

import pandas as pd
import numpy as np
from random import random
from numpy.random import randn

from bokeh.plotting import figure, show, curdoc
from bokeh.models import Slider, CustomJS, Range1d, Button
from bokeh.layouts import column
from bokeh.plotting import figure, curdoc
import os

slider_slope = Slider(title = 'Slope', start = 0, end = 1, value = 0.5, step = 0.1)
slider_intercept = Slider(title = 'Intercept', start = 0, end = 20, value = 10, step = 1)

s = slider_slope.value  # slope.
i = slider_intercept.value  # intercept.

x = np.linspace(-40, 20, 200)
y = [(s * xx + i) for xx in x]

p = figure(title = "simple line example", plot_height = 500, plot_width = 600, y_range = Range1d(start = -80, end = 40), background_fill_color = '#efefef')
r = p.line(x, y, color = "red", line_width = 1.5, alpha = 0.8)  # 1st line. This line can be controlled by sliders.
q = p.line(x, 2 * x + 1.2, color = "blue", line_width = 1.9, alpha = 0.2)  # 2nd line. This could be actuals.

def update(attr, old, new):
    s = slider_slope.value  # slope.
    i = slider_intercept.value  # intercept
    x = r.data_source.data['x'];
    y = []

    for value in x:
        y.append((s * value) + i)

    r.data_source.data['y'] = y

# create a callback that will save the slider settings to a csv file when the button is clicked.
def callback():
    os.chdir("C:\\Users") # Change the working directory to where I want to save the csv.
    mydf = pd.DataFrame.from_dict({'slope':[0],'intercept':[0]}) # Create a DataFrame using pandas, based on a dictionary definition. Set the values to be 0 by default.
    mydf.loc[0] = [slider_slope.value, slider_intercept.value] # Assign the first row to slope and intercept.
    mydf.to_csv('slider.csv',index=True) # Write to the csv the final values of the button.  

# add a button widget and configure with the call back
button = Button(label="Save slope and intercept to csv")
button.on_click(callback)

slider_slope.on_change('value', update)
slider_intercept.on_change('value', update)

layout = column(p, slider_slope, slider_intercept, button)
curdoc().add_root(layout)
show(layout, notebook_handle = True) # Launch the chart in the web browser.

答案 1 :(得分:0)

我没有Jupyter Notebook,所以这两个示例都是纯Bokeh应用程序,第一个示例使用JS回调,第二个示例使用Python回调(Bokeh v1.0.4)。

import numpy as np
from bokeh.plotting import figure, show
from bokeh.models import ColumnDataSource, Slider, CustomJS, Range1d
from bokeh.layouts import column

slider_slope = Slider(start = 0, end = 1, value = 0.5, step = 0.1)
slider_intercept = Slider(start = 0, end = 20, value = 10, step = 1)

slider_code = '''   i = slider_intercept.value
                    s = slider_slope.value
                    x = r.data_source.data['x'];
                    y = [];

                    for (index = 0; index < x.length; index ++)
                        y.push((s * x[index]) + i);

                    r.data_source.data['y'] = y
                    r.data_source.change.emit(); '''

s = slider_slope.value  # slope.
i = slider_intercept.value  # intercept.

x = np.linspace(-40, 20, 200)
y = [(s * xx + i) for xx in x]

p = figure(title = "simple line example", plot_height = 500, plot_width = 600, y_range = Range1d(start = -80, end = 40), background_fill_color = '#efefef')
r = p.line(x, y, color = "red", line_width = 1.5, alpha = 0.8)  # 1st line. This line can be controlled by sliders.
q = p.line(x, 2 * x + 1.2, color = "blue", line_width = 1.9, alpha = 0.2)  # 2nd line.

slider_callback = CustomJS(args = dict(slider_slope = slider_slope,
                                slider_intercept = slider_intercept,
                                r = r), code = slider_code)

slider_slope.callback = slider_callback
slider_intercept.callback = slider_callback

layout = column(p, slider_slope, slider_intercept)
show(layout, notebook_handle = True)

您可以使用Python回调将其轻松转换为Bokeh服务器应用程序:

import numpy as np
from bokeh.plotting import figure, show, curdoc
from bokeh.models import Slider, CustomJS
from bokeh.layouts import column

slider_slope = Slider(title = 'Slope', start = 0, end = 1, value = 0.5, step = 0.1)
slider_intercept = Slider(title = 'Intercept', start = 0, end = 20, value = 10, step = 1)

s = slider_slope.value  # slope.
i = slider_intercept.value  # intercept.

x = np.linspace(-40, 20, 200)
y = [(s * xx + i) for xx in x]

p = figure(title = "simple line example", plot_height = 500, plot_width = 600, y_range = Range1d(start = -80, end = 40), background_fill_color = '#efefef')
r = p.line(x, y, color = "red", line_width = 1.5, alpha = 0.8)  # 1st line. This line can be controlled by sliders.
q = p.line(x, 2 * x + 1.2, color = "blue", line_width = 1.9, alpha = 0.2)  # 2nd line.

def update(attr, old, new):
    s = slider_slope.value  # slope.
    i = slider_intercept.value  # intercept
    x = r.data_source.data['x'];
    y = []

    for value in x:
        y.append((s * value) + i)

    r.data_source.data['y'] = y

slider_slope.on_change('value', update)
slider_intercept.on_change('value', update)

layout = column(p, slider_slope, slider_intercept)
curdoc().add_root(layout)

结果:

enter image description here