Reputation: 877
I am trying to convey a large amount of scientific data seamlessly with the help of sliders.
I am beginning with Bokeh and have close to no knowledge in javascript. I tried to setup a first approach to be able to slide through two images, but I cannot get the image to refresh.
Suppose I have 1.png and 2.png in my folder.
from bokeh.io import vform
from bokeh.models import CustomJS, ColumnDataSource, Slider
from bokeh.plotting import Figure, output_file, show
output_file('image.html')
source = ColumnDataSource(data=dict(url=['1.png']))
p = Figure(x_range=(0,1), y_range=(0,1))
callbackimg = CustomJS(args=dict(source=source), code="""
var data = source.get('data');
var f = cb_obj.get('value')
old = data['url'][0]
data['url'][0]= old.replace("1","f")
source.trigger('change');
""")
p.image_url('url',source=source, x=0, y=1,w=1,h=1)
slider = Slider(start=1, end=2, value=1, step=1, title="image number", callback=callbackimg)
layout = vform(slider, p)
show(layout)
I adapted examples from Bokeh Widget Doc for the slider and working with images in bokeh.
My idea is that the slider, through the callbackimg snippet, will modify the source which contains the url, i.e the name of the image to load. I thought, for now, that a simple access to the string in the source, and a replacement through the current value of the slider (so it should jump from 1.png to 2.png as the slider goes from 1 to 2) should do the trick.
However, nothing is changing. I suspect I am doing something wrong in the Javascript snippet.
Thanks for any feedback
EDIT: I edited the code according to the suggestions of @bigreddot, but now the slider shows simply an empty figure when sliding in position '2'. EDIT2: Solved the issue, in my answer below
Upvotes: 3
Views: 2184
Reputation: 3061
Another option is to create in advance a data source with all the image urls and use the slider to select the correct image.
Given the image names:
import pandas as pd
imgs = ['1.png', '2.png', '3.png']
df = pd.DataFrame(imgs, columns=['imgs'])
print(df.head())
imgs
0 1.png
1 2.png
2 3.png
You can do:
from bokeh.layouts import layout
from bokeh.plotting import figure, show
from bokeh.models import CustomJS, Slider, ColumnDataSource
source = ColumnDataSource(df)
p = figure(x_range=(0,1), y_range=(0,1))
im = p.image_url(url=[source.data['imgs'][0]], x=0, y=1, w=1, h=1)
cb = CustomJS(args=dict(im=im, source=source), code="""
im.data_source.data['url'] = [source.data['imgs'][cb_obj.value]];
im.data_source.change.emit();
""")
slider = Slider(start=0, end=2, value=0, step=1, title="image number", callback=cb)
show(layout([slider, p]))
Upvotes: 0
Reputation: 188
I have created a sample example where image changes as per the slider value. If slider value is 1 then app will display map-marker.png image and if value is 2 then app will display google_logo.png image.
With the help of Div you can do this task very easily.
Steps:
After creating folder in above mentioned location copy google_logo.png and map-marker.png images to /usr/local/lib/python2.7/dist-packages/bokeh/server/static/image this location. And save the following code as app.py and run it using bokeh serve app.py --show command.
from bokeh.io import curdoc
from bokeh.layouts import row
from bokeh.models.widgets import Slider, Div
def change_img(attr, old, new):
if slider.value == 1:
logo.text = """<img src="static/image/map-marker.png" alt="logo">"""
else:
logo.text = """<img src="static/image/google_logo.png" alt="logo">"""
slider = Slider(start=1, end=2, value=1, title="image number")
slider.on_change('value', change_img)
logo = Div(text="""<img src="static/image/logo.png" alt="logo">""")
curdoc().add_root(row(slider,logo))
curdoc().title = "Image Demo"
tested on bokeh version 0.12.13
Upvotes: 1
Reputation: 877
@bigreddot answer is correct, but var f is a number, so in the replace, I need to write f.toString(10). Code that does what I want:
from bokeh.io import vform
from bokeh.models import CustomJS, ColumnDataSource, Slider
from bokeh.plotting import Figure, output_file, show
output_file('image.html')
source = ColumnDataSource(data=dict(url=['1.png']))
p = Figure(x_range=(0,1), y_range=(0,1))
callbackimg = CustomJS(args=dict(source=source), code="""
var data = source.get('data');
var f = cb_obj.get('value')
old = data['url'][0]
data['url'][0]= old.replace("1",f.toString(10))
source.trigger('change');
""")
p.image_url('url',source=source, x=0, y=1,w=1,h=1)
slider = Slider(start=1, end=2, value=1, step=1, title="image number", callback=callbackimg)
layout = vform(slider, p)
show(layout)
Upvotes: 2
Reputation: 34568
The problem is this:
url = data['url'][0]
url.replace("1","f")
The replace
method returns a new string (which you immediately discard), so you are not actually changing anything in the column data source. You need something like:
old = data['url'][0]
data['url'] = old.replace("1","f")
Upvotes: 1