Search code examples
bokeh

Asynchronous Streaming To Embedded Bokeh Server


The question: My python application starts a Bokeh server as described in this article:

http://matthewrocklin.com/blog/work/2017/06/28/simple-bokeh-server

now, I want to visualize streaming data, which is generated in the python application, which started the bokeh server and push it asynchronously to my bokeh visualization. Is this possible? How?


Solution

  • Yes it is possible. I think the best option is to have a separate thread that fills the data-bucket and on the other side the Bokeh periodic update function (like in example you mentioned) that accesses that data and streams it to the browser. See this simple example below. But have also a look at Bokeh documentation about updating plot data from threads.

    import random, time
    from tornado.ioloop import IOLoop
    from bokeh.server.server import Server
    from bokeh.application import Application
    from bokeh.application.handlers.function import FunctionHandler
    from bokeh.plotting import figure, ColumnDataSource
    from threading import Thread
    
    class BokehApp():
        plot_data = []
        last_data_length = None
    
        def __init__(self):
            thread = Thread(target = self.startDataAcquisition)
            thread.start()
    
            io_loop = IOLoop.current()
            server = Server(applications = {'/myapp': Application(FunctionHandler(self.make_document))}, io_loop = io_loop, port = 5001)
            server.start()
            server.show('/myapp')
            io_loop.start()
    
        def startDataAcquisition(self):
            while True:
                self.plot_data.append({'x': [random.random()], 'y': [random.random()], 'color': [random.choice(['red', 'blue', 'green'])]})
                time.sleep(5)
    
        def make_document(self, doc):
            source = ColumnDataSource({'x': [], 'y': [], 'color': []})
            fig = figure(title = 'Streaming Circle Plot!', sizing_mode = 'scale_both')
            fig.circle(source = source, x = 'x', y = 'y', color = 'color', size = 10)
    
            def update():
                if self.last_data_length is not None and self.last_data_length != len(self.plot_data):
                    source.stream(self.plot_data[-1])
                self.last_data_length = len(self.plot_data)
    
            doc.add_root(fig)
            doc.add_periodic_callback(update, 1000)
    
    if __name__ == '__main__':
        app = BokehApp()