-
-
Save tmmsartor/057ffb990f6f7fdb5221b789029b42ce to your computer and use it in GitHub Desktop.
Bokeh example about runtime events
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#! /usr/bin/python3.6 | |
# author: tmmsartor@gmail.com | |
import numpy as np | |
import time | |
from functools import partial | |
from bokeh.plotting import figure, curdoc | |
from bokeh.layouts import column, row | |
from bokeh.models.widgets import Button | |
from bokeh.models.glyphs import Line | |
from bokeh.models import ColumnDataSource | |
from bokeh.client import push_session | |
from threading import Thread | |
from tornado import gen | |
# server | |
from multiprocessing import Process | |
from bokeh.application import Application | |
from bokeh.server.server import Server | |
def bokeh_server(): | |
server = Server({"/": Application()}) | |
print("Start Server") | |
server.run_until_shutdown() | |
p = Process(target=bokeh_server) | |
p.start() | |
class StatefulPlot: | |
def __init__(self): | |
self.doc = curdoc() | |
self.session = push_session(self.doc) | |
# object connected | |
self.source1 = ColumnDataSource( | |
dict(time=[], line1=[], line2=[]) | |
) | |
self.x1 = 0 | |
self.x2 = 0 | |
x = np.linspace(0, 200, 100) | |
x1 = np.linspace(-2*np.pi, 0, 100) | |
y = np.cos(x1) | |
self.tools = "xpan,xwheel_zoom,xbox_zoom,reset" | |
self.h = 300 | |
self.w = 700 | |
self.source1 = ColumnDataSource(dict(time=[], line1=[])) | |
self.source2 = ColumnDataSource(dict(time=x1, line2=y)) | |
p1 = figure(plot_height=self.h, plot_width=self.w, tools=self.tools) | |
p1.line(x='time', y='line1', alpha=0.2, line_width=3, | |
color='navy', source=self.source1) | |
p1.line(x, y, alpha=0.2, line_width=3, color='orange') | |
p1.toolbar.logo = None | |
self.p2 = figure(plot_height=self.h, plot_width=self.w, | |
tools=self.tools) | |
self.p2.line(x, np.zeros(x.shape), alpha=0.2, color='navy') | |
self.p2.line(x='time', y='line2', alpha=0.8, line_width=2, | |
color='orange', source=self.source2) | |
p1.x_range.follow = "end" | |
p1.x_range.follow_interval = 100 | |
self.p2.x_range.follow = "end" | |
self.p2.x_range.follow_interval = 100 | |
button = Button(label='pause/play') | |
button.on_click(self.start_stop) | |
rewind = Button(label='rewind') | |
rewind.on_click(self.rewind) | |
add_fig = Button(label='add/del new fig') | |
add_fig.on_click(self.add_fig_f) | |
add_line = Button(label='add_line') | |
add_line.on_click(self.add_line_f) | |
self.thread = None | |
self.start = 0 | |
self.rewind = 0 | |
self.x = 0 | |
self.new_fig = 0 | |
self.buttons = row(add_line, button, rewind, add_fig) | |
self.plots = column(p1, self.p2) | |
self.doc.add_root(self.buttons) | |
self.doc.add_root(self.plots) | |
def start_stop(self): | |
if not self.start: | |
print("starting") | |
self.start = 1 | |
self.doc.add_next_tick_callback(self._do_task) | |
else: | |
print("stopping") | |
self.start = 0 | |
return | |
def add_fig_f(self): | |
self.doc.add_next_tick_callback(self._add_fig) | |
def add_line_f(self): | |
self.doc.add_next_tick_callback(self._add_line) | |
def rm_fig_f(self): | |
self.doc.add_next_tick_callback(self._rm_fig) | |
def rewind(self): | |
if not self.rewind: | |
print("rewind start") | |
self.rewind = 1 | |
self.doc.add_next_tick_callback(self._do_rewind) | |
else: | |
print("rewind stopping") | |
self.rewind = 0 | |
return | |
@gen.coroutine | |
def _add_line(self): | |
print("Adding line") | |
x = np.linspace(0, 100, 100) | |
self.source3 = ColumnDataSource(dict(time=x, line=np.sin(x))) | |
self.p2.add_glyph(self.source3, Line()) | |
@gen.coroutine | |
def _add_fig(self): | |
if not self.new_fig: | |
f = figure(plot_height=self.h, plot_width=self.w, | |
tools=self.tools) | |
x = np.linspace(0, 100, 100) | |
f.line(x, np.zeros(x.shape), alpha=0.2, line_width=3, color='navy') | |
self.plots.children.append(f) | |
self.new_fig = 1 | |
else: | |
self.new_fig = 0 | |
self.plots.children.pop(2) | |
@gen.coroutine | |
def _do_rewind(self): | |
self.x -= 2 | |
start = time.time() | |
self.source2.data.update({ | |
"time": self.source2.data["time"][:-100], | |
"line2": self.source2.data["line2"][:-100]}) | |
print(f"elapsed {time.time() - start}") | |
yield gen.sleep(0.1) | |
if self.rewind: | |
self.doc.add_next_tick_callback(self._do_rewind) | |
@gen.coroutine | |
def _do_task(self): | |
x = np.linspace(self.x*np.pi, (self.x+2)*np.pi, 100) | |
self.x += 2 | |
new_data = dict( | |
time=x, | |
line2=np.cos(x) | |
) | |
self.source2.stream(new_data) | |
yield gen.sleep(0.1) | |
# Add callback again | |
if self.start: | |
self.doc.add_next_tick_callback(self._do_task) | |
fig = StatefulPlot() | |
# module wise work | |
@gen.coroutine | |
def plot_this(i): | |
x = np.linspace(i*0.5, (i+1)*0.5, 10) | |
new_data = dict( | |
time=x, | |
line1=np.sin(x)) | |
fig.source1.stream(new_data) | |
def my_loop(): | |
t = 0 | |
while 1: | |
time.sleep(0.2) | |
# Add callback again | |
fig.doc.add_next_tick_callback(partial(plot_this, t)) | |
t += 1 | |
# show session | |
fig.session.show() | |
# run forever | |
thread = Thread(target=fig.session.loop_until_closed) | |
thread.start() | |
# do stuff | |
my_loop() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment