Reputation: 55
I am trying to make a Bokeh plot that updates whenever it receives data. I am using add_next_tick_callback(listener)
to get data posted with queries using curl "http://localhost:5006/mviz/?vpom=0.9&rpom=0.9"
. Data is read correctly when sent using curl but,
source.stream
.source.data
each time increasing the number of rows. But even on hitting the curl
command above multiple times, it only prints same number of rows each time (with only four rows, one new row appended to the three rows at initialization).Following is the script I have in file mviz.py
that I run by calling bokeh serve mviz.py
using Bokeh version 1.0.4:
from bokeh.layouts import column
from bokeh.models import ColumnDataSource
from bokeh.plotting import figure, curdoc
from bokeh.server.server import Server
source = ColumnDataSource({"vpom": [0.1, 0.2, 0.3], "rpom": [0.2, 0.3, 0.4]})
fig = figure(title='Streaming Circle Plot!', sizing_mode='scale_width',
x_range=[0, 1], y_range=[0, 1])
fig.circle(source=source, x='vpom', y='rpom', size=10)
curdoc().add_root(column(fig))
curdoc().title = "Now with live updating!"
def listener():
req = curdoc().session_context
if req is not None:
args = req.request.arguments
print "recv", args
if args:
source.stream({k:map(float,v) for k,v in args.items()}, 100)
print source.data
curdoc().add_next_tick_callback(listener)
Can someone please point issues with the above script and comment if this is the correct way to use Boekh for this usecase.
Upvotes: 0
Views: 1200
Reputation: 34568
There are a couple of problems/misunderstandings with this approach:
Every time a request is made to a Bokeh server, a Session
is created, as well as a Document
for that session. The Document
is the collection of all your objects (plots, widgets, etc) that the Bokeh server keeps in sync automatically beteeen Python and JS. Where does this document come from? The app code is executed to generate it. To reiterate another way: the app code is run anew every time a connection is made.
An immediate consequence is that, as written, nothing at all will "persist" or "accumulate" between HTTP requests with the code above. Every new connection is a blank slate. The entire app code will atill get run every new connection, but it could first look up and load any previous data from your external data store as a first step.
An add_next_tick_callback
at the top level is going to execute even before the session is loaded in the browser. There is no reason to put an add_next_tick_callback
at the top level (i.e. outside of some other button or widget's callback code) because the end effect is the same as just putting the code inside the callback function at the top level. It finishes running before anything ever even hits the user's browser.
So what might you do to implement something like this?
If you really only want to hit the Bokeh app URL with query arguments, your app code needs to be explicitly responsible for saving and loading state between requests. This could be in a flat file that it reads/writes to, or via some external database it connects to.
Alternatively, you could embed the Bokeh serve as a library in which case you could add new request handlers for new endpoints of your own to the underlying Tornado application. These handlers could persist/accumulate the state (while the Bokeh server is running). How to push/communicate updates to any existing open sessions is a more involved question that depends on your requirements.
Upvotes: 1