You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Bokeh饼图如何通过js_on_change实现复选框控制动态筛选重绘

问题解决说明

你代码的核心问题有3个:

  1. 没有把饼图绑定的ColumnDataSource传给CustomJS回调,回调修改的数据和饼图渲染用的数据源完全无关
  2. 回调仅生成了选中的水果字典,没有重新计算饼图需要的角度、百分比字段,也没有把新数据同步给数据源
  3. Bokeh仅会在ColumnDataSource的data属性发生变更时触发图形重绘,你之前的操作没有触发这个更新机制
完整可运行修改代码
from bokeh.layouts import column
from bokeh.models import ColumnDataSource, CustomJS, CheckboxGroup
from math import pi
import pandas as pd
from bokeh.io import show
from bokeh.plotting import figure
from bokeh.transform import cumsum 
from bokeh.palettes import Category10

# 原始数据
fruits = {'Apple': 10,
          'Mango': 24,
          'Graphes': 9,
          'Banana': 15,
          'Peach': 6,
          'Pinapple' : 7,
}
fruit_list = list(fruits.keys())
color_list = Category10[len(fruits)]

# 初始化数据源,显式创建ColumnDataSource方便后续传入回调
data = pd.Series(fruits).reset_index(name='value').rename(columns={'index':'Fruits'})
data['angle'] = data['value']/data['value'].sum() * 2 * pi
data['color'] = color_list
data['percent'] = data['value']/sum(fruits.values()) * 100
source = ColumnDataSource(data)

# 绘制饼图
p = figure(plot_height=350, title='Pie chart', tools="hover",
           tooltips="@Fruits: @value  : @percent{0.2f} %", x_range=(-0.5,1.0))
p.wedge(x=0, y=1, radius=0.4,
        start_angle=cumsum('angle', include_zero=True), end_angle=cumsum('angle'),
        line_color="white", fill_color='color', legend='Fruits', source=source)
p.axis.axis_label = None
p.axis.visible = False
p.grid.grid_line_color = None

# 交互回调
callback = CustomJS(args = {'source':source, 'fruit_list':fruit_list, 'color_list':color_list, 'raw_fruits':fruits},
code="""
const active = cb_obj.active;
// 初始化各字段数组
let Fruits = [], value = [], color = [], angle = [], percent = [];
let total = 0;

// 第一步:收集选中的水果和对应数值,先算总和
active.forEach(idx => {
    let fruit = fruit_list[idx];
    Fruits.push(fruit);
    let val = raw_fruits[fruit];
    value.push(val);
    color.push(color_list[idx]);
    total += val;
})

// 第二步:计算每个水果的角度和百分比
let current_angle = 0;
value.forEach(val => {
    let ang = val / total * 2 * Math.PI;
    angle.push(ang);
    percent.push(val / total * 100);
})

// 第三步:更新数据源,触发重绘
source.data = {
    Fruits: Fruits,
    value: value,
    color: color,
    angle: angle,
    percent: percent
}
"""
)

# 复选框
checkbox_group = CheckboxGroup(labels=fruit_list, active = [0,1,2,3,4,5])
checkbox_group.js_on_change('active', callback)

layout = column(checkbox_group, p)
show(layout)

内容的提问来源于stack exchange,提问作者vinay tiwari

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.04 13:18:03