1

I recently built a dashboard that includes bokeh widgets. Every time I run it I need to go to anaconda prompt and type

bokeh serve --show myapp.py

Is there a more friendly way to do this without open up a cmd window? I'm currently on Windows 7 and soon to be updated to Windows 10.

Thanks!

chen
  • 97
  • 1
  • 10

2 Answers2

0

This is explained here in Bokeh documentation: Running a Bokeh Server

And here is one example for Bokeh v1.1.0:

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
import random

def make_document(doc):
    source = ColumnDataSource({'x': [], 'y': [], 'color': []})

    def update():
        new = {'x': [random.random()], 'y': [random.random()], 'color': [random.choice(['red', 'blue', 'green'])]}
        source.stream(new)

    doc.add_periodic_callback(update, 1000)

    fig = figure(title = 'Streaming Circle Plot!', x_range = [0, 1], y_range = [0, 1])
    fig.circle(source = source, x = 'x', y = 'y', color = 'color', size = 10)

    doc.title = "Now with live updating!"
    doc.add_root(fig)

apps = {'/': Application(FunctionHandler(make_document))}

io_loop = IOLoop.current()
server = Server(applications = {'/': Application(FunctionHandler(make_document))}, io_loop = io_loop, port = 5001)
server.start()
server.show('/')
io_loop.start()
Tony
  • 7,767
  • 2
  • 22
  • 51
  • Hello Tony, thanks for the reply. I'm trying to read your code as well as bokeh's doc. But I'm really new to coding, can you offer more insights into what role does each function play? – chen Apr 18 '19 at 17:10
  • Also, my project is based off one of bokeh's example. https://github.com/bokeh/bokeh/tree/master/examples/app/movies. I'm wondering which of those functions mentioned in your example are necessary for this project. Thank you! – chen Apr 18 '19 at 17:13
  • See second answer – Tony Apr 18 '19 at 19:00
0

And here is the requested code converted from Bokey movies example (Bokeh v1.1.0):

from tornado.ioloop import IOLoop
from bokeh.server.server import Server
from bokeh.application import Application
from bokeh.application.handlers.function import FunctionHandler

from os.path import dirname, join
import numpy as np
import pandas.io.sql as psql
import sqlite3 as sql
from bokeh.plotting import figure
from bokeh.layouts import layout, column
from bokeh.models import ColumnDataSource, Div
from bokeh.models.widgets import Slider, Select, TextInput
from bokeh.io import curdoc
from bokeh.sampledata.movies_data import movie_path

def make_document(doc):
    conn = sql.connect(movie_path)
    query = open(join(dirname(__file__), 'query.sql')).read()
    movies = psql.read_sql(query, conn)

    movies["color"] = np.where(movies["Oscars"] > 0, "orange", "grey")
    movies["alpha"] = np.where(movies["Oscars"] > 0, 0.9, 0.25)
    movies.fillna(0, inplace = True)  # just replace missing values with zero
    movies["revenue"] = movies.BoxOffice.apply(lambda x: '{:,d}'.format(int(x)))

    with open(join(dirname(__file__), "razzies-clean.csv")) as f:
        razzies = f.read().splitlines()
    movies.loc[movies.imdbID.isin(razzies), "color"] = "purple"
    movies.loc[movies.imdbID.isin(razzies), "alpha"] = 0.9

    axis_map = {
        "Tomato Meter": "Meter",
        "Numeric Rating": "numericRating",
        "Number of Reviews": "Reviews",
        "Box Office (dollars)": "BoxOffice",
        "Length (minutes)": "Runtime",
        "Year": "Year",
    }

    desc = Div(text = open(join(dirname(__file__), "description.html")).read(), width = 800)

    # Create Input controls
    reviews = Slider(title = "Minimum number of reviews", value = 80, start = 10, end = 300, step = 10)
    min_year = Slider(title = "Year released", start = 1940, end = 2014, value = 1970, step = 1)
    max_year = Slider(title = "End Year released", start = 1940, end = 2014, value = 2014, step = 1)
    oscars = Slider(title = "Minimum number of Oscar wins", start = 0, end = 4, value = 0, step = 1)
    boxoffice = Slider(title = "Dollars at Box Office (millions)", start = 0, end = 800, value = 0, step = 1)
    genre = Select(title = "Genre", value = "All",
                   options = open(join(dirname(__file__), 'genres.txt')).read().split())
    director = TextInput(title = "Director name contains")
    cast = TextInput(title = "Cast names contains")
    x_axis = Select(title = "X Axis", options = sorted(axis_map.keys()), value = "Tomato Meter")
    y_axis = Select(title = "Y Axis", options = sorted(axis_map.keys()), value = "Number of Reviews")

    # Create Column Data Source that will be used by the plot
    source = ColumnDataSource(data = dict(x = [], y = [], color = [], title = [], year = [], revenue = [], alpha = []))

    TOOLTIPS = [
        ("Title", "@title"),
        ("Year", "@year"),
        ("$", "@revenue")
    ]

    p = figure(plot_height = 600, plot_width = 700, title = "", toolbar_location = None, tooltips = TOOLTIPS)
    p.circle(x = "x", y = "y", source = source, size = 7, color = "color", line_color = None, fill_alpha = "alpha")

    def select_movies():
        genre_val = genre.value
        director_val = director.value.strip()
        cast_val = cast.value.strip()
        selected = movies[
            (movies.Reviews >= reviews.value) &
            (movies.BoxOffice >= (boxoffice.value * 1e6)) &
            (movies.Year >= min_year.value) &
            (movies.Year <= max_year.value) &
            (movies.Oscars >= oscars.value)
        ]
        if (genre_val != "All"):
            selected = selected[selected.Genre.str.contains(genre_val) == True]
        if (director_val != ""):
            selected = selected[selected.Director.str.contains(director_val) == True]
        if (cast_val != ""):
            selected = selected[selected.Cast.str.contains(cast_val) == True]
        return selected

    def update():
        df = select_movies()
        x_name = axis_map[x_axis.value]
        y_name = axis_map[y_axis.value]

        p.xaxis.axis_label = x_axis.value
        p.yaxis.axis_label = y_axis.value
        p.title.text = "%d movies selected" % len(df)
        source.data = dict(
            x = df[x_name],
            y = df[y_name],
            color = df["color"],
            title = df["Title"],
            year = df["Year"],
            revenue = df["revenue"],
            alpha = df["alpha"],
        )

    controls = [reviews, boxoffice, genre, min_year, max_year, oscars, director, cast, x_axis, y_axis]
    for control in controls:
        control.on_change('value', lambda attr, old, new: update())

    inputs = column(*controls)
    l = layout([
        [desc],
        [inputs, p],
    ])

    update()  # initial load of the data

    doc.add_root(l)
    doc.title = "Movies"

io_loop = IOLoop.current()
server = Server(applications = {'/': Application(FunctionHandler(make_document))}, io_loop = io_loop, port = 5001)
server.start()
server.show('/')
io_loop.start()
Tony
  • 7,767
  • 2
  • 22
  • 51
  • Thanks for making the efforts! Although I got '500: Internal Server Error' showing up on my browser after I ran it. Any idea what might be wrong here? – chen Apr 22 '19 at 13:56
  • The code works for Bokeh v1.1.0 and Tornado v4.5.3. If you use different versions please consider upgrading Bokeh or downgrading Tornado. – Tony May 01 '19 at 11:57