我有一个脚本,使用 easgui 提示用户输入他们感兴趣的系统的名称和感兴趣的参数以及其他几个配置。然后继续为所有参数和系统创建 Bokeh(使用版本“2.4.3”)仪表板,其中每个系统都有一个仪表板,其中包含该仪表板所需的所有可用参数数据。在我的脚本末尾,我定义了一个菜单,以便用户可以选择在生成整个内容后他们看到的系统仪表板。但是菜单在某种意义上不起作用,因为它只显示默认值并且从中选择任何选项都不会改变实际显示的内容。
下面是最小的可复制示例:
import os
import pandas as pd
import numpy as np
import os
from bokeh import plotting
from bokeh import layouts
from bokeh.models.tools import HoverTool
from bokeh.models.widgets.markups import Div
from bokeh.models import CustomJS, TapTool, Button, ColumnDataSource, Select, DatetimeTickFormatter
from bokeh.io import curdoc
systems=['TestSys1', 'TestSys2']
dashboards={}
dash_sources={}
index_list = list(range(len(systems)))
sys_figs={}
for idx in index_list:
system=systems[idx]
save_loc=r'.\Test'
customer='TestCustomer'
all_or_view='all'
conceal_params=False
sys_figs[system] = {}
system_params=['p_1', 'p_2', 'p_3', 'p_4']
first_logtime = '1990-01-01 00:00:00.000'
last_logtime = '2023-01-01 00:00:00.000'
# Initialise objects for tracking things that need to be tracked
indices = list(range(len(system_params)))
shared_x_range = None
figures = {}
sources = {param:ColumnDataSource() for param in system_params}
for idx in indices:
param_name = system_params[idx]
param_id = system_params[idx]
param_name_normalised = param_name.replace(' ', '')
raw_label=param_name
dates = pd.date_range(start=first_logtime, end=last_logtime, freq='D')
data = np.random.randn(len(dates), 1)
df = pd.DataFrame(data=data, index=dates, columns=[param_name])
df.index.name='LogTime'
src=ColumnDataSource(df)
prepared_param_data={'source':src}
# Save the param_last_log_time for this param so that the update function can then query
# the database and stream and plot only data that has LogTime greater than the previous
# param_last_log_time, if any.
sources[param_name].data = dict(prepared_param_data['source'].data)
## Set shared_x_range ##
if len(figures) > 0:
first_plotted_param = list(figures.keys())[0]
shared_x_range=figures[first_plotted_param].x_range
else:
shared_x_range=None
## Instantiate figures ##
if shared_x_range != None:
sys_figs[system][param_name] = plotting.figure(x_axis_label='DateTime',
y_axis_label=raw_label,
x_range=shared_x_range,
x_axis_type='datetime',
title=raw_label,
toolbar_location='right')
else:
sys_figs[system][param_name] = plotting.figure(x_axis_label='DateTime',
y_axis_label=raw_label,
x_axis_type='datetime',
title=raw_label,
toolbar_location='right')
## Plot line glyph for data ##
sys_figs[system][param_name].line(x='LogTime',
y=raw_label,
source=src,
color='#47ed00',
line_alpha=0.7,
muted_alpha=0.2,
legend_label=raw_label)
param_fig = sys_figs[system][param_name]
if param_fig != None:
figures[param_name_normalised] = param_fig
# Construct dashboard
figure_columns=[layouts.column(figures['p_1'], figures['p_2']),
layouts.column(figures['p_3'], figures['p_4'])]
dashboard = layouts.row(figure_columns)
dashboard_title = Div(text=f"{system} Dashboard", style={'font-size':'30px', 'color':'black'}, width=3000)
dashboard = layouts.column(dashboard_title, dashboard)
dashboards[system]=dashboard
dash_sources[system]=sources
print(f'Dashboard for {system} created.')
# Create a dropdown menu with options for each dashboard key
dashboard_keys = list(dashboards.keys())
dashboard_select = Select(title="Select Dashboard", value=dashboard_keys[0], options=dashboard_keys)
# Create a ColumnDataSource to hold the selected dashboard
selected_dashboard = ColumnDataSource(data={'dashboard': [dashboards[dashboard_keys[0]]]})
def update_dashboard(attrname, old, new):
selected_dashboard.data = {'dashboard': [dashboards[new]]}
selected_dashboard.change.emit()
dashboard_select.on_change('value', update_dashboard)
# Create a layout with the dropdown menu and the selected dashboard
layout = layouts.column(dashboard_select, selected_dashboard.data['dashboard'][0])
# Add a callback to update the layout when the selected_dashboard changes
def update_layout(attrname, old, new):
layout.children[1] = selected_dashboard.data['dashboard'][0]
selected_dashboard.on_change('data', update_layout)
# Add the layout to the current document
curdoc().add_root(layout)
然后我通过从 Anaconda 命令行运行
bokeh serve --show "C:\Users\a00555655\OneDrive - ONEVIRTUALOFFICE\Documents\Test\StackExchangeExample.py" --port 1535
来提供服务。
为什么下拉菜单不起作用?
我尝试了很多东西,但没有成功。问题似乎出在回调上,但我不确定我哪里出错了。
这就是问题所在:
selected_dashboard.change.emit()
JS和Python控制台都报错(你没看到这些吗?):
AttributeError:ColumnDataSource 的意外属性“更改”,可能的属性是数据、js_event_callbacks、js_property_callbacks、name、selected、selection_policy、subscribed_events、syncable 或 tags
如果我删除该行,示例代码对我有用。
仅供参考,如果您为
source.data
分配一个全新的值,例如source.data = new_data
,它会被自动检测到——无需其他操作即可触发更新。仅当“就地”更新列时才需要调用emit
(不鼓励这样做)。