I want to send additional data to a bokeh event handler (e.g. an on_change or on_click method). A minimal example that increments or decrements an integer is below (I run this app with 'bokeh serve --show app.py). I had to write separate event handlers that do almost identical things in this example. To write this app with just one event handler function, I need to pass additional data or the event handler must know the calling object. How do I do that?
from bokeh.plotting import curdoc
from bokeh.models.widgets import Button, Paragraph
from bokeh.layouts import widgetbox
minus = Button(label='-')
plus = Button(label='+')
text = Paragraph(text='0')
def minus_callback():
text.text = str(int(text.text) - 1)
def plus_callback():
text.text = str(int(text.text) + 1)
minus.on_click(minus_callback)
plus.on_click(plus_callback)
# I would prefer to just use one callback and pass additional data to it:
# minus.on_click(callback, action='decrement')
# plus.on_click(callback, action='increment')
layout = widgetbox(minus, plus, text)
curdoc().add_root(layout)
The standard functools.partial facility that is built into python works fine with Bokeh callbacks.
from functools import partial
from bokeh.plotting import curdoc
from bokeh.models.widgets import Button, Paragraph
from bokeh.layouts import widgetbox
minus = Button(label='-')
plus = Button(label='+')
text = Paragraph(text='0')
def callback(foo):
print(foo)
minus.on_click(partial(callback, foo="minus"))
plus.on_click(partial(callback, foo="plus"))
layout = widgetbox(minus, plus, text)
curdoc().add_root(layout)
Extending on #bigreddot's answer, this code outputs the updated value to the text attribute of the Paragraph object.
from functools import partial
from bokeh.plotting import curdoc
from bokeh.models.widgets import Button, Paragraph
from bokeh.layouts import column
def callback(update):
text.text = str(int(text.text) + update)
minus = Button(label='-')
plus = Button(label='+')
text = Paragraph(text='0')
minus.on_click(partial(callback, update=-1))
plus.on_click(partial(callback, update=+1))
layout = column(minus, plus, text)
curdoc().add_root(layout)
Related
This discussion provides a workaround:
import streamlit as st
from bokeh.models.widgets import Button
from bokeh.models import CustomJS
from streamlit_bokeh_events import streamlit_bokeh_events
import pandas as pd
text_to_be_copied = "Some text"
copy_dict = {"content": text_to_be_copied}
copy_button = Button(label="Copy Text")
copy_button.js_on_event("button_click", CustomJS(args=copy_dict, code="""
navigator.clipboard.writeText(content);
"""))
no_event = streamlit_bokeh_events(
copy_button,
events="GET_TEXT",
key="get_text",
refresh_on_update=True,
override_height=75,
debounce_time=0)
However, the button looks different than the usual streamlit buttons and cannot be used like those for example like this:
if st.button("Some button"):
st.write("Some reaction")
Is there a way to modify the streamlit buttons such that they copy a text to the clipboard?
You must use pyperclip library to achieve what you want.
import pyperclip
text_to_be_copied = 'The text to be copied to the clipboard.'
pyperclip.copy(text_to_be_copied)
Hello people of the internet,
So i tried to make a simple interactive visualisation where I can use a slide bar to change the radius of some dots on a plot using python and bokeh.
However, when I try to make an update function it seems that I get an error message saying:
500: Internal Server Error.
I also tried to make the update function such that it made a meaningless variable but the error message would still come up and show no plot or slider.
So my question is: How can I make a slider that will change the radius of the circle using a bokeh server?
# imports
from bokeh.io import push_notebook, show, output_notebook
from bokeh.server.server import Server
from bokeh.application import Application
from bokeh.application.handlers.function import FunctionHandler
from bokeh.layouts import column
from bokeh.models import Slider
from bokeh.plotting import figure
output_notebook()
# Webpage function, this function will be called upon creating the webpage
def make_page(doc):
#source = ColumnDataSource(data=dict(radius=20))
size=20
def update(attr, old, new):
s = new
radius = s
size.data = dict(radius)
plot = figure(plot_width=400, plot_height=400)
plot.circle([1,2], [4,8], size = size, color='blue', alpha=0.5);
slider = Slider(start=0, end=30, value=20, step=1, title="Number")
slider.on_change(update)
# adding the slider and plot to the document shown on the webpage
# all elements of the webpage have to be added below
doc.add_root(column(slider, plot))
# creating the application and running the server local, (http://localhost:5000), port 5000 can be changed
apps = {'/': Application(FunctionHandler(make_page))}
server = Server(apps, port=5000)
server.start()
Thanks in advance!
Already fixed it, here is the code
# imports
from bokeh.io import push_notebook, show, output_notebook
from bokeh.server.server import Server
from bokeh.application import Application
from bokeh.application.handlers.function import FunctionHandler
from bokeh.layouts import column
from bokeh.models import Slider
from bokeh.plotting import figure
from bokeh.plotting import ColumnDataSource
output_notebook()
# Webpage function, this function will be called upon creating the webpage
def make_page(doc):
#source = ColumnDataSource(data=dict(radius=20))
data = dict(
x=[1,2],
y=[4,8],
radius=[20,20]
)
source = ColumnDataSource(data=data)
def update(attr, old, new):
radius = new
new_data = dict(
x=[1,2],
y=[4,8],
radius=[radius,radius]
)
source.data= new_data
plot = figure(plot_width=400, plot_height=400)
plot.circle(x='x', y='y', size = 'radius', color='blue', alpha=0.5, source=source);
slider = Slider(start=0, end=30, value=20, step=1, title="Number")
slider.on_change('value', update)
# adding the slider and plot to the document shown on the webpage
# all elements of the webpage have to be added below
doc.add_root(column(slider, plot))
# creating the application and running the server local, (http://localhost:5000), port 5000 can be changed
apps = {'/': Application(FunctionHandler(make_page))}
server = Server(apps, port=5000)
server.start()
I have a Bokeh app in which I change the layout during runtime. This app contains a TextInput object which should always be focused, but this focus is lost when I change the layout. Is there a way to automatically set the focus back periodically or after each layout change?
I only found this related discussion, but was unable to apply it to my particular case.
Here is a minimal example where the focus is lost each time you type in the TextInput, triggering a layout change:
from bokeh.plotting import curdoc
from bokeh.models import TextInput
from bokeh.models.widgets import Div
from bokeh.layouts import column
def text_input_change(attr, old, new):
layout.children[1] = column(text, text_input)
doc = curdoc()
text = Div(text='Test', width=100)
text_input = TextInput(value='', title='', width=100)
text_input.on_change('value_input', text_input_change)
layout = column(text, text_input)
doc.add_root(layout)
The bokeh application below is intended to generate a random dataset when the button is pushed. I am trying to serve the app using the bokeh.client style, where there is one session that may be shared between simultaneous viewers.
If I include the line: curdoc().add_root(column(p,button)) the plot will not be in the browser. I get a blank page with happy messages in JS console. If I remove it, I get a static plot, with no button. Can anyone explain what's wrong with my approach here?
I should add that the app works in the other server style with multiple distinct sessions. There I call bokeh serve myapp.py and don't make calls to the session object.
import numpy as np
from bokeh.plotting import figure, curdoc
from bokeh.layouts import column
from bokeh.models import Button
from bokeh.client import push_session, pull_session
points = 100*np.random.rand(3,100)
points_x = points[0].tolist()
points_y = points[1].tolist()
p = figure(x_range=(0,100), y_range=(0,100))
circle_p = p.circle(x = points_x,
y = points_y,
size = 20,
color = "navy",
alpha = 0.5)
ds = circle_p.data_source
#callback function to update circles
def button_callback():
new_data = dict()
new_points = 100*np.random.rand(3,100)
new_data['x'] = new_points[0].tolist()
new_data['y'] = new_points[1].tolist()
new_data['z'] = new_points[2].tolist()
ds.data = new_data
#Add the button widget thingie to trigger the update
button = Button(label="Update")
button.on_click(button_callback)
# Put the button and plot in a layout in document
curdoc().add_root(column(p,button))
#create a session
session = push_session(curdoc())
session.show(p)
session.loop_until_closed()
You just want
session.show()
not
session.show(p)
Because you want to show the whole document, not just the plot. The first version works for me with Bokeh 0.12.6 (the latter also kind of works, but the plot is duplicated twice. My guess is you are using an older version that also had some layout bugs)
I would like to add objects dynamically on the bokeh server. The example I am trying to run is the following bokeh server app:
from bokeh.layouts import column
from bokeh.plotting import curdoc
from bokeh.models import Button
def add_button():
print("adding button")
curdoc().add_root(column(button, button2))
button = Button(label="Start", button_type="success")
button.on_click(add_button)
button2 = Button(label="Next", button_type="success")
curdoc().add_root(column(button))
Many thanks for any help.
Did you want to keep adding a new button each time?
if so try this :
from bokeh.layouts import column, layout
from bokeh.plotting import curdoc
from bokeh.models import Button
from bokeh.models.widgets import Div
def add_button():
print("adding button")
layout.children.append(Button(label="Hi I am another button", button_type="success"))
button = Button(label="Click to add a button", button_type="success")
button.on_click(add_button)
layout = layout([[button]])
curdoc().add_root(layout)
If you only wanted to add a new button once, then just append Button2.