Skip to content

Instantly share code, notes, and snippets.

@2torus
Created May 27, 2018 15:05
Show Gist options
  • Save 2torus/36605e7c2e7a6ef672e4b345ad2c88d7 to your computer and use it in GitHub Desktop.
Save 2torus/36605e7c2e7a6ef672e4b345ad2c88d7 to your computer and use it in GitHub Desktop.
Bokeh DataTable doesn't support AjaxDataSource
import numpy as np
from datetime import timedelta
from functools import update_wrapper, wraps
from math import sin
from random import random
from six import string_types
from bokeh.models.widgets import DataTable, TableColumn
from bokeh.plotting import figure, show, output_file
from bokeh.models.sources import AjaxDataSource
output_file("ajax_source.html", title="ajax_source.py example")
source = AjaxDataSource(data_url='http://localhost:5050/data',
polling_interval=100, data={'x': [], 'y': []})
colx = TableColumn(field="x", title="x")
coly = TableColumn(field="y", title="y")
table = DataTable(source=source, columns=[colx, coly])
try:
from flask import Flask, jsonify, make_response, request, current_app
except ImportError:
raise ImportError("You need Flask to run this example!")
show(table)
#########################################################
# Flask server related
#
# The following code has no relation to bokeh and it's only
# purpose is to serve data to the AjaxDataSource instantiated
# previously. Flask just happens to be one of the python
# web frameworks that makes it's easy and concise to do so
#########################################################
def crossdomain(origin=None, methods=None, headers=None,
max_age=21600, attach_to_all=True,
automatic_options=True):
"""
Decorator to set crossdomain configuration on a Flask view
For more details about it refer to:
http://flask.pocoo.org/snippets/56/
"""
if methods is not None:
methods = ', '.join(sorted(x.upper() for x in methods))
if headers is not None and not isinstance(headers, string_types):
headers = ', '.join(x.upper() for x in headers)
if not isinstance(origin, string_types):
origin = ', '.join(origin)
if isinstance(max_age, timedelta):
max_age = max_age.total_seconds()
def get_methods():
options_resp = current_app.make_default_options_response()
return options_resp.headers['allow']
def decorator(f):
@wraps(f)
def wrapped_function(*args, **kwargs):
if automatic_options and request.method == 'OPTIONS':
resp = current_app.make_default_options_response()
else:
resp = make_response(f(*args, **kwargs))
if not attach_to_all and request.method != 'OPTIONS':
return resp
h = resp.headers
h['Access-Control-Allow-Origin'] = origin
h['Access-Control-Allow-Methods'] = get_methods()
h['Access-Control-Max-Age'] = str(max_age)
requested_headers = request.headers.get(
'Access-Control-Request-Headers'
)
if headers is not None:
h['Access-Control-Allow-Headers'] = headers
elif requested_headers:
h['Access-Control-Allow-Headers'] = requested_headers
return resp
f.provide_automatic_options = False
return update_wrapper(wrapped_function, f)
return decorator
app = Flask(__name__)
x = list(np.arange(0, 6, 0.1))
y = [sin(xx) + random() for xx in x]
@app.route('/data', methods=['GET', 'OPTIONS', 'POST'])
@crossdomain(origin="*", methods=['GET', 'POST'], headers=None)
def hello_world():
x.append(x[-1]+0.1)
y.append(sin(x[-1])+random())
return jsonify(x=x[-500:], y=y[-500:])
if __name__ == "__main__":
app.run(port=5050)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment