curdoc().add_root() causes bokeh plot rendering to fail silently - python

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)

Related

shiny for Python using add_layer for Popus from ipyleaflet

The bounty expires in 2 days. Answers to this question are eligible for a +50 reputation bounty.
MYaseen208 wants to draw more attention to this question.
I want to use m.add_layer for Popus from ipyleaflet in shiny for python (as given here). However, it is not working as expected. My minimum working example is given below:
from shiny import App, render, ui
from shinywidgets import output_widget, reactive_read, register_widget
from ipywidgets import HTML
from ipyleaflet import Map, Marker, Popup
app_ui = ui.page_fluid(
output_widget("m")
)
def server(input, output, session):
center = (52.204793, 360.121558)
m = Map(center=center, zoom=9, close_popup_on_click=False)
message1 = HTML()
message1.value = "Try clicking the marker!"
# Popup with a given location on the map:
popup = Popup(
location=center,
child=message1,
close_button=False,
auto_close=False,
close_on_escape_key=False
)
m.add_layer(popup) # This line is not working
register_widget("m", m)
app = App(app_ui, server)
Wondering what basic am I missing here?

Updating a graph using a slider with python and bokeh

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()

Bokeh Server - Change color of glyph on select with Tap Tool

I want to use the TapTool with Bokeh server to run a callback with some additional functionality.
The glyphs I want to select are on top of a background image.
If I select a glyph with the TapTool the glyph keeps its opacity but all other glyphs get reduced in opacity. The problem is that those glyphs can not be seen well on the background image when they become more opace.
Is there a way to leave the alpha values of the glyphs all at 100% and instead change the color of the selected glyphs?
Here is some example code as a start that I found here
from bokeh import plotting as bplt
from bokeh import layouts as blayouts
from bokeh import models as bmodels
from bokeh import io as bio
fig = bplt.figure(tools="tap")
source = bmodels.ColumnDataSource(dict(x=[0,1], y=[0,1]))
r = fig.circle('x', 'y', source=source, size=10)
def handler(attr, old, new):
print('attr: {} old: {} new: {}'.format(attr, old, new))
# r.data_source.on_change('selected', handler)
r.data_source.selected.on_change('indices', handler)
bio.curdoc().add_root(blayouts.layout([[fig]]))
Here is a working example for bokeh 2.0.1:
from bokeh import plotting as bplt
from bokeh import layouts as blayouts
from bokeh import models as bmodels
from bokeh import io as bio
fig = bplt.figure(tools="tap")
source = bmodels.ColumnDataSource(dict(x=[0,1], y=[0,1]))
r = fig.circle('x', 'y', source=source, size=10, color='#000000',
# set visual properties for selected glyphs
selection_color="#2bff00",
# set visual properties for non-selected glyphs
nonselection_fill_alpha=1.0,
nonselection_fill_color="#000000")
def handler(attr, old, new):
print('attr: {} old: {} new: {}'.format(attr, old, new))
# r.data_source.on_change('selected', handler)
r.data_source.selected.on_change('indices', handler)
bio.curdoc().add_root(blayouts.layout([[fig]]))

Python Bokeh send additional parameters to widget event handler

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)

matplotlib show figure again

When using matplotlib:
from matplotlib import pyplot as plt
figure = plt.figure()
ax = figure.add_subplot(111)
ax.plot(x,y)
figure.show() # figure is shown in GUI
# How can I view the figure again after I closed the GUI window?
figure.show() # Exception in Tkinter callback... TclError: this isn't a Tk application
figure.show() # nothing happened
So my questions are:
How can I get the previous plot back if I have called figure.show()?
Is there a more convenient alternative to figure.add_suplot(111) if I have multiple figures and thus from pylab import *; plot(..); show() seems not a solution I'm looking for.
And what I eagerly want is
showfunc(stuff) # or
stuff.showfunc()
where stuff is an object containing all the plots arranged in one picture, and showfunc is STATELESS(I mean, each time I call it, I behaves as if it's first time called). Is this possible when working with matplotlib?
I can't find a satisfactory answer, so I handle this problem by writing a custom Figure class extending matplotlib.figure.Figure and providing a new show() method, which create a gtk.Window object each time called.
import gtk
import sys
import os
import threading
from matplotlib.figure import Figure as MPLFigure
from matplotlib.backends.backend_gtkagg import FigureCanvasGTKAgg as FigureCanvas
from matplotlib.backends.backend_gtkagg import NavigationToolbar2GTKAgg as NaviToolbar
class ThreadFigure(threading.Thread):
def __init__(self, figure, count):
threading.Thread.__init__(self)
self.figure = figure
self.count = count
def run(self):
window = gtk.Window()
# window.connect('destroy', gtk.main_quit)
window.set_default_size(640, 480)
window.set_icon_from_file(...) # provide an icon if you care about the looks
window.set_title('MPL Figure #{}'.format(self.count))
window.set_wmclass('MPL Figure', 'MPL Figure')
vbox = gtk.VBox()
window.add(vbox)
canvas = FigureCanvas(self.figure)
vbox.pack_start(canvas)
toolbar = NaviToolbar(canvas, window)
vbox.pack_start(toolbar, expand = False, fill = False)
window.show_all()
# gtk.main() ... should not be called, otherwise BLOCKING
class Figure(MPLFigure):
display_count = 0
def show(self):
Figure.display_count += 1
thrfig = ThreadFigure(self, Figure.display_count)
thrfig.start()
Make this file as start file of IPython. And
figure = Figure()
ax = figure.add_subplot(211)
... (same story as using standard `matplotlib.pyplot` )
figure.show()
# window closed accidentally or intentionally...
figure.show()
# as if `.show()` is never called
Works! I never touched GUI programming, and don't know if this would have any side-effect. Comment freely if you think something should be done that way.

Categories

Resources