How to add hover tool to the add_layout in python bokeh? - python

I want to add a hover tool to an Arrow in add_layout. However, it seems that the hover tool does not add anything to it. Here is my code:
from bokeh.models import Arrow, VeeHead, ColumnDataSource, HoverTool
from bokeh.plotting import figure, output_file, show
output_file("arrow.html", title="arrow.py example")
p = figure(plot_width=600, plot_height=600)
p.circle(x=[0, 1, 3], y=[0, 5, 0.7], radius=0.1,
color=["navy", "yellow", "red"], fill_alpha=0.1)
data = ColumnDataSource({"x_start": [0],
"x_end": [2],
"y_start": [0],
"y_end": [1]})
p.add_layout(Arrow(end=VeeHead(size=35), line_color="red", x_start="x_start", y_start="y_start", x_end="x_end", y_end="y_end", source = data, name="arrow"))
hover_tool = HoverTool(tooltips=[('x_start', '#x_start'), ("x_end", "#x_end"), ("y_start", "#y_start")], names=["arrow"])
p.add_tools(hover_tool)
show(p)

Arrows are annotations, not glyphs, and do not support hit-testing. It is not possible to attach a hover tool to an arrow.

Related

Hide annotation alongside legend in Bokeh

I have an interactive Bokeh plot that's able to hide certain circle plots when clicking on the Legend. Now when I disable a plot by clicking on it, the plotted circles disappear but the annotations remain. Could someone explain to me how I can toggle those on/off all together or does anyone have a quick fix?
Here's a picture when it's toggled off:
I plot the circles + legend with the following code:
q.circle('lng', 'lat', source = source2, name='vri', color='red', size=5, hover_line_color="black", legend_label = 'VRI')
vri_labels = LabelSet(x='lng', y='lat', text='kruispuntn', x_offset=5, y_offset=5, source=source2, text_font_size = '10pt')
q.legend.location = "bottom_left"
q.legend.click_policy="hide"
q.add_layout(vri_labels)
show(q)
You can link the visible property via a CustomJS callback:
from bokeh.io import show
from bokeh.models import ColumnDataSource, LabelSet, CustomJS
from bokeh.plotting import figure
p = figure()
cds = ColumnDataSource(data=dict(x=[0, 1], y=[0, 1], z=[1, 0]))
for var, params in [('y', {}),
('z', {'color': 'green'})]:
renderer = p.circle('x', var, source=cds, legend_label=var, size=20, **params)
label_set = LabelSet(x='x', y=var, text=var, source=cds, x_offset=5, y_offset=5)
p.add_layout(label_set)
renderer.js_on_change('visible', CustomJS(args=dict(ls=label_set),
code="ls.visible = cb_obj.visible;"))
p.legend.click_policy = 'hide'
show(p)

Bokeh line 'disappearing' when using hover_line_alpha

I'm using the Bokeh package to plot a line chart.
I want a given line to bolden (alpha to increase) when I hover over it.
I added a hover tool and then added "hover_line_alpha = 0.6" in my line chart.
However when I hover over points on a given line, the line disappears altogether!
Can you help me fix this?
Code below so you can see my logic.
Thanks,
Ross
# Code in Question
from bokeh.io import output_notebook, show, output_file
from bokeh.plotting import figure
from bokeh.models import ColumnDataSource, HoverTool
output_notebook()
# set out axes
x = 'time_rnd'
y = 'count'
# set colour palette
col_brew = ['#8dd3c7','#ffffb3','#bebada','#fb8072','#80b1d3','#fdb462','#b3de69','#fccde5','#d9d9d9','#bc80bd','#ccebc5','#ffed6f']
# map out figure
plot = figure(tools='box_select, lasso_select, save' ,x_axis_type='datetime')
# add HoverTool
hover_info = [('time', '#hover_time'),
('word', '#word'),
('count', '#count')]
hover = HoverTool(names=['use'],tooltips=hover_info,
mode='mouse',
show_arrow=True
)
plot.add_tools(hover)
### FOR LOOP OF PLOT [THIS IS WHERE THE ISSUE MANIFESTS]
for i in top_wds_test:
df_eng_word = df_eng_timeline[df_eng_timeline['word']==i]
source = ColumnDataSource(df_eng_word)
plot.line(x, y, line_width = 3,
line_alpha = 0.1, line_color=col_brew[top_wds.index(i)],
hover_line_alpha = 0.6,
#hover_line_color = 'black',
#hover_line_color = col_brew[top_wds.index(i)],
source = source, legend=i, name = 'use'
)
plot.circle(x, y, fill_color='white', size=5,
selection_color='green',
nonselection_fill_color='grey',nonselection_fill_alpha=0.4,
hover_color='red',
source = source, name = 'use')
# add legend
plot.legend.location = "top_left"
plot.legend.label_text_font_style = 'bold'
# materialize the plot
show(plot)
There seems to be an issue when the renderers share a data source. However, this works (with Bokeh >= 0.13.0) if you let Bokeh create a new separate source for each glyph:
from bokeh.models import ColumnDataSource
from bokeh.plotting import figure, show
p = figure(tools="hover", tooltips="$name: #$name")
data=dict(x=[1,2,3], y1=[2,6,5], y2=[6,2,3])
p.line('x', 'y1', color="navy", line_width=3, source=data,
alpha=0.1, hover_color="navy", hover_alpha=0.6, name="y1")
p.line('x', 'y2',color="firebrick", line_width=3, source=data,
alpha=0.1, hover_color="firebrick", hover_alpha=0.6, name="y2")
show(p)

Bokeh: tools='reset' makes the graph fixed

I wanted to put only reset toolbar on by the graph, so I was trying to like
logo=None, tools='reset'
The reset button is actually placed but instead the graph is fixed and cannot move from original position.
How can I improve it?
To move the plot you just need to add the PanTool. Check this minimal example:
from bokeh.models import ColumnDataSource
from bokeh.plotting import figure
from bokeh.io import curdoc
plot = figure(
width=300,
height=300,
tools='pan,reset',
logo=None,
)
x = [1, 2, 3, 4]
y = [4, 3, 2, 1]
source = ColumnDataSource(data=dict(x=x, y=y))
plot.circle(
x='x',
y='y',
source=source,
size=5,
fill_alpha=1.0,
fill_color='green',
line_color=None,
)
curdoc().add_root(plot)
Run this with bokeh serve --show example.py

Add hover to Bokeh without losing rest of tools

So with Bokeh I can do something like this to create a hover option:
From bokeh.models import HoverTool #add hover functionality
Hover = HoverTool(tooltips=[(name1:#column1), (name2:#columns2)])
Plot = figure(tools=[hover])
Plot.circle(x,y,hover_color=’red’)
However, by doing so, I lose the standard tools you get when you call figure() like pan, box_zoom, wheel_zoom, etc. I know I can add them back 1 by 1 inside the figure(tools=[]), but is there a way to only add hover to the rest of the default tools of figure(), after it is defined??
Thanks!
Use the add_tools() method, as outlined in the docs:
https://docs.bokeh.org/en/latest/docs/user_guide/tools.html#specifying-tools
Slightly modified example from the docs:
from bokeh.plotting import figure, output_file, show
from bokeh.models import HoverTool
output_file("toolbar.html")
# create a new plot with the toolbar below
p = figure(plot_width=400, plot_height=400,
title=None, toolbar_location="below")
p.circle([1, 2, 3, 4, 5], [2, 5, 8, 2, 7], size=10)
p.add_tools(HoverTool())
show(p)

Interactively change glyphs in bokeh plot

I'm trying to generate a bokeh application which allows the user to change the glyphs of a plot. Unfortunately, the glyphs don't change after calling on_change() method of the dropdown button although I'm able to change the axis label in a similar way. However, changing plot.glyph outside of the called function works fine.
from bokeh.layouts import layout
from bokeh.models import ColumnDataSource
from bokeh.models.widgets import Dropdown
from bokeh.plotting import figure
from bokeh.io import curdoc
from bokeh.models.markers import Cross, Circle
source=ColumnDataSource(dict(x=[1,2,3],y=[4,5,6]))
fig=figure()
plot=fig.circle(x='x', y='y',source=source)
fig.xaxis.axis_label='This is a label'
#this changes the glyphs from circle to cross
plot.glyph=Cross(x='x', y='y', size=20, line_color='firebrick',
fill_color='firebrick', line_alpha=0.8, fill_alpha=0.3)
def update_plot(attr,old,new):
if dropdown.value=='cross':
#this changes the axis label but not the glyphs
fig.xaxis.axis_label='Label changed'
plot.glyph=Cross(x='x', y='y', size=20, line_color='firebrick',
fill_color='firebrick', line_alpha=0.8, fill_alpha=0.3)
elif dropdown.value=='circle':
#this also only changes the axis label but not the glyphs
fig.xaxis.axis_label='Label changed again'
plot.glyph=Circle(x='x', y='y', size=20, line_color='firebrick',
fill_color='firebrick', line_alpha=0.8, fill_alpha=0.3)
menu=[('Circle','circle'),('Cross', 'cross')]
dropdown=Dropdown(label="Select marker", menu=menu, value='circle')
dropdown.on_change('value', update_plot)
lay_out=layout([fig, dropdown])
curdoc().add_root(lay_out)
I'm not sure about the exact reasons why your approach doesn't work, but this one works:
from bokeh.io import curdoc
from bokeh.layouts import layout
from bokeh.models import ColumnDataSource
from bokeh.models.widgets import Dropdown
from bokeh.plotting import figure
source = ColumnDataSource(dict(x=[1, 2, 3], y=[4, 5, 6]))
plot = figure()
renderers = {rn: getattr(plot, rn)(x='x', y='y', source=source,
**extra, visible=False)
for rn, extra in [('circle', dict(size=10)),
('line', dict()),
('cross', dict(size=10)),
('triangle', dict(size=15))]}
def label_fn(item):
return 'Select marker ({})'.format(item)
menu = [('No renderer', None)]
menu.extend((rn.capitalize(), rn) for rn in renderers)
dropdown = Dropdown(label=label_fn(None), menu=menu, value=None)
def update_plot(attr, old, new):
dropdown.label = label_fn(new)
for renderer_name, renderer in renderers.items():
renderer.visible = (renderer_name == new)
dropdown.on_change('value', update_plot)
lay_out = layout([plot, dropdown])
curdoc().add_root(lay_out)
Basically, I create all necessary renderers beforehand, and then just switch visible flag of each one.
Also, note the correct terminology. What you're calling a plot, is not actually a plot but a glyph renderer. And plot and figure are basically the same thing.

Categories

Resources