Created
May 27, 2018 15:05
-
-
Save 2torus/36605e7c2e7a6ef672e4b345ad2c88d7 to your computer and use it in GitHub Desktop.
Bokeh DataTable doesn't support AjaxDataSource
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
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