我正在尝试在Django应用程序中创建一个散景图,以绘制运动员的游泳事件。我正在绘制持续时间(时间游泳)与日期(游泳日期)。我们的想法是拥有一个绘图,并能够使用SelectBox小部件来选择图形上显示的事件。问题是,当我在CallbackJS函数中更改数据源时,图表不会更新,而是变为空白。
数据来自表格
的事件对象class Event(models.Model):
swimmer = models.ForeignKey(Swimmer, on_delete=models.SET_NULL, null=True, blank=True)
team = models.ForeignKey(Team, on_delete=models.CASCADE, null=True, blank=True)
name = models.CharField(max_length=50, null=True, blank=True)
gender = models.CharField(max_length=1, choices=GENDER_CHOICE, null=True, blank=True)
event = models.CharField(max_length=10, choices=EVENT_CHOICE)
time = models.DurationField()
place = models.IntegerField(null=True, blank=True)
date = models.DateField(null=True)
首先,我遍历每个事件(当前为17个),并列出日期(datetime.date)和时间(datetime.timedelta)字段。未修改的字段分别用于x和y,并且稍微编辑值(主要是类型转换为字符串)以用于悬停工具。如果特定事件没有数据,则data_source{}
条目设置为None
。
示例数据:
data_source = {
'x_50_free': [date(2017,9,7), date(2017,9,8)]
'y_50_free': [timedelta(seconds=22.96), timedelta(seconds=22.32)]
'date_50_free': ['9/7/2017', '9/8/2017']
'time_50_free': ['00:22.96', '00:22.32']
'x_100_free': [date(2017,9,7)]
'y_100_free': [timedelta(seconds=49.86)]
'date_100_free': ['9/7/2017']
'time_100_free': ['00:49.86']
}
然后,我绘制一条初始线,以便在页面加载时显示一条线。
source = ColumnDataSource(data=dict(
x=data_source['x_'+first_event],
y=data_source['y_'+first_event],
date=data_source['date_'+first_event],
time=data_source['time_'+first_event]
))
plot.line('x', 'y', source=source)
我在回调函数中更新源数据
callback = CustomJS(args=dict(source=source), code="""
data = %s;
f = cb_obj.value;
if (f == "50 Freestyle") {
source.data['x'] = data.x_50_free;
source.data['y'] = data.y_50_free;
source.data['date'] = data.date_50_free;
source.data['time'] = data.time_50_free;
} else if (f == "100 Freestyle") {
source.data['x'] = data.x_100_free;
source.data['y'] = data.y_100_free;
source.data['date'] = data.date_100_free;
source.data['time'] = data.time_100_free;
}
...
} else if (f == "400 IM") {
source.data['x'] = data.x_400_im;
source.data['y'] = data.y_400_im;
source.data['date'] = data.date_400_im;
source.data['time'] = data.time_400_im;
}
source.change.emit();
""" % json.dumps(data_source, cls=DatetimeEncoder)) # encoder to handle datetimes for x-axis
据我所知,source.change.emit()
用于更新ColumnDataSource。这似乎有效,因为我能够将source.data[]
记录到控制台并根据Select
窗口小部件选项查看它更新,但是图表本身不会更新它只是空白。如何更新绘图以反映源数据的变化?
答案 0 :(得分:1)
你的例子既不简洁也不完整。一个完整的例子可以立即运行。最小的一个遗漏了与您的问题无关的方面。因此,我不确定我是否理解了所有内容,但我试图对其进行调整。以下是一些可以帮助您入门的代码:
from bokeh.io import show, output_file, output_notebook
from bokeh.models import ColumnDataSource
from bokeh.plotting import figure
from bokeh.layouts import column, widgetbox
from bokeh.models import CustomJS, Select
output_file("swim_plot.html")
first_event = "_50_free"
second_event = "_100_free"
data_source = {
'x': [date(2017,9,3), date(2017,9,4)],
'y': [0, 0],
'x_50_free': [date(2017,9,7), date(2017,9,8)],
'y_50_free': [22.23, 24.34],
'x_100_free': [date(2017,9,12), date(2017,9,14)],
'y_100_free': [23.22, 25,12]
}
source = ColumnDataSource(data=data_source)
callback = CustomJS(args=dict(source=source), code="""
data = source.data;
f = cb_obj.value;
if (f == "_50_free") {
data['x'] = data.x_50_free;
data['y'] = data.y_50_free;
} else if (f == "_100_free") {
data['x'] = data.x_100_free;
data['y'] = data.y_100_free;
}
source.change.emit();
""")
select = Select(title="Option:", value="default", options=["default",
first_event, second_event])
select.js_on_change('value', callback)
plot = figure(plot_width=400, plot_height=400, x_axis_type='datetime')
plot.line('x', 'y', source=source)
show(column(widgetbox(select),plot))
我在代码中看到的主要问题是ColumnDataSource的定义。在将所有数据添加到data_dict时,只需将其中的一部分添加到源中。因此,尝试访问JS回调中缺少的数据将失败。