# Import Stuffs
from bokeh.plotting import figure, output_file, show, save
from bokeh.layouts import column, row
from bokeh.models import ColumnDataSource, CustomJS
from bokeh.models import HoverTool, TapTool
from bokeh.models import ColumnDataSource, CustomJS, Div
from bokeh.events import Tap
from IPython.display import IFrame
from IPython.core.display import display, HTML
import tempfile
import numpy as np
# Deepnote Show function for Bokhe
def bokeh_deepnote_show(plot):
tmp_output_filename = tempfile.NamedTemporaryFile(suffix='.html').name
output_file(tmp_output_filename)
save(plot)
f = open(tmp_output_filename, "r")
display(HTML(f.read()))
source = ColumnDataSource(dict(
x=[1, 2, 2, 1, 2, 3],
y=[1, 1, 2, 2, 3, 2],
))
fig = figure(title='My Coordinates: Hover over a Circle',
plot_height=500, plot_width=600,
x_range=(0, 4), y_range=(0, 4))
hover_glyph = fig.circle(source=source, x='x', y='y',
color='green', size=10, alpha=0.5,
hover_fill_color='black', hover_alpha=0.5)
tooltips = [('x', '@x'), ('y', '@y')]
fig.add_tools(HoverTool(tooltips=tooltips, renderers=[hover_glyph]))
bokeh_deepnote_show(fig)
source = ColumnDataSource(dict(
x=[1, 2, 2, 1, 2, 3],
y=[1, 1, 2, 2, 3, 2],
))
fig = figure(title='My Coordinates: Click Anywhere to Mark',
plot_height=500, plot_width=600,
x_range=(0, 4), y_range=(0, 4))
hover_glyph = fig.circle(source=source, x='x', y='y',
color='green', size=10, alpha=0.5,
hover_fill_color='black', hover_alpha=0.5)
tooltips = [('x', '@x'), ('y', '@y')]
fig.add_tools(HoverTool(tooltips=tooltips, renderers=[hover_glyph]))
callback = CustomJS(args=dict(source=source), code="""
// change data source from Callback
var mainData = source.data;
mainData['x'].push(cb_obj.x);
mainData['y'].push(cb_obj.y);
console.log(mainData);
source.change.emit();
""")
fig.js_on_event(Tap, callback)
bokeh_deepnote_show(fig)
source = ColumnDataSource(dict(
x=[1, 2, 2, 1, 2, 3],
y=[1, 1, 2, 2, 3, 2],
))
selected = ColumnDataSource(dict(
x=[],
y=[],
))
fig = figure(title='My Coordinates: Select the Circles',
plot_height=500, plot_width=600,
x_range=(0, 4), y_range=(0, 4))
hover_glyph = fig.circle(source=source, x='x', y='y',
color='green', size=10, alpha=0.5,
hover_fill_color='black', hover_alpha=0.5)
tooltips = [('x', '@x'), ('y', '@y')]
fig.add_tools(HoverTool(tooltips=tooltips, renderers=[hover_glyph]))
fig.add_tools(TapTool())
callback = CustomJS(args=dict(source=source, selected=selected), code="""
// get data source from Callback args
let data = Object.assign({}, source.data);
let indices = Object.assign([], source.selected.indices);
data.x = indices.map(i => data.x[i]);
data.y = indices.map(i => data.y[i]);
selected.data = data;
console.log(selected.data);
selected.change.emit();
""")
fig.js_on_event(Tap, callback)
bokeh_deepnote_show(fig)
# function for the custom display and custom callback
def display_event(div, selected=[], attributes=[], style='float:left;clear:left;'):
return CustomJS(args=dict(div=div, source=selected), code="""
// mark graph (circle)
var data = source.data;
data['x'].push(cb_obj.x);
data['y'].push(cb_obj.y);
console.log(data);
source.change.emit();
// display tap event graph coordinates
var attrs = %s; var args = [];
for (var i = 0; i<attrs.length; i++) {
args.push(attrs[i] + '=' + Number(cb_obj[attrs[i]]).toFixed(2));
}
var line = "<span style=%r><b>" + cb_obj.event_name + "</b>(" + args.join(", ") + ")</span>\\n";
var text = div.text.concat(line);
var lines = text.split("\\n")
if (lines.length > 35)
lines.shift();
div.text = lines.join("\\n");
""" % (attributes, style))
# Generate Random Data
start = 0.0
stop = 15.1
step = 0.1
float_range_array = list(np.arange(start, stop, step))
x_data = ["%.2f" % num for num in float_range_array]
y_data = list(np.random.uniform(0, 400, size=(151)))
# Create Bokeh Data Source for the Line Data
source = ColumnDataSource(
dict(
x=x_data,
y=y_data,
)
)
# Create Empty Data Source for Tap Data
selected = ColumnDataSource(
dict(
x=[],
y=[],
)
)
# Create Bokeh Figure
fig = figure(
title="My Line: Click to Return x/y",
plot_height=400,
plot_width=900,
x_range=(0, 15),
y_range=(0, 400)
)
# Create Line Plot (for line data)
line_glyph = fig.line(source=source, x="x", y="y", line_width=2, alpha=0.5)
# Create Circle Plot (for tap data)
circle_glyph = fig.circle(source=selected, x="x", y="y", color="red", size=5, alpha=1)
# Add Tap Tool (for tapping)
fig.add_tools(TapTool(renderers=[line_glyph]))
# Add Hover for Circle Plot (so we can also hover over the tap mark to view coordinates)
fig.add_tools(HoverTool(tooltips=[("x", "@x"), ("y", "@y")], renderers=[circle_glyph]))
# Create Div for Tap Feedback (this will sit next too the graph)
div = Div(
width=1000,
height=fig.plot_height,
height_policy="fixed",
style={"padding": "6px", "padding-top": "16px", "font_size":"14px"},
)
# Create Event Callback on Tap and use the CustomJS callback we have defined in the display_event function
fig.js_on_event(Tap, display_event(div, selected=selected, attributes=["x", "y"]))
# Viz Layout
layout = row(fig, div)
# Deepnote Show
bokeh_deepnote_show(layout)