# ```python
# # hello.py
#
# from bokeh.io import curdoc
# from bokeh.layouts import column
# from bokeh.models.widgets import TextInput, Button, Paragraph
#
# # create some widgets
# button = Button(label="Say HI")
# input = TextInput(value="Bokeh")
# output = Paragraph()
#
# # add a callback to a widget
# def update():
# output.text = "Hello, " + input.value
# button.on_click(update)
#
# # create a layout for everything
# layout = column(button, input, output)
#
# # add the layout to curdoc
# curdoc().add_root(layout)
# ```
# Copy this code to a script `hello.py` and run it with the Bokeh server.
# ## Linking Plots and Widgets
#
# Lets take a look at a more involved example that links several widgets to a plot.
# In[ ]:
from numpy.random import random
from bokeh.layouts import column, row
from bokeh.plotting import figure
from bokeh.models import ColumnDataSource, Select, TextInput
def get_data(N):
return dict(x=random(size=N), y=random(size=N), r=random(size=N) * 0.03)
COLORS = ["black", "firebrick", "navy", "olive", "goldenrod"]
def modify_doc(doc):
source = ColumnDataSource(data=get_data(200))
p = figure(tools="", toolbar_location=None)
r = p.circle(x='x', y='y', radius='r', source=source,
color="navy", alpha=0.6, line_color="white")
select = Select(title="Color", value="navy", options=COLORS)
input = TextInput(title="Number of points", value="200")
def update_color(attrname, old, new):
r.glyph.fill_color = select.value
select.on_change('value', update_color)
def update_points(attrname, old, new):
N = int(input.value)
source.data = get_data(N)
input.on_change('value', update_points)
layout = column(row(select, input, width=400), row(p))
doc.add_root(layout)
show(modify_doc)
# In[ ]:
# EXERCISE: add more widgets to change more aspects of this plot
# # Streaming Data
#
# It is possible to efficiently stream new data to column data sources by using the ``stream`` method. This method accepts two argmuments:
# * ``new_data`` — a dictionary with the same structure as the column data source
# * ``rollover`` — a maximum column length on the client (earlier data is dropped) *[optional]*
#
# If no ``rollover`` is specified, data is never dropped on the client and columns grow without bound.
#
# It is often useful to use periodic callbacks in conjuction with streaming data The ``add_periodic_callback`` method of ``curdoc()`` accepts a callback function, and a time interval (in ms) to repeatedly execute the callback.
#
# In[ ]:
from math import cos, sin
from bokeh.models import ColumnDataSource
def modify_doc(doc):
p = figure(match_aspect=True)
p.circle(x=0, y=0, radius=1, fill_color=None, line_width=2)
# this is just to help the auto-datarange
p.rect(0, 0, 2, 2, alpha=0)
# this is the data source we will stream to
source = ColumnDataSource(data=dict(x=[1], y=[0]))
p.circle(x='x', y='y', size=12, fill_color='white', source=source)
def update():
x, y = source.data['x'][-1], source.data['y'][-1]
# construct the new values for all columns, and pass to stream
new_data = dict(x=[x*cos(0.1) - y*sin(0.1)], y=[x*sin(0.1) + y*cos(0.1)])
source.stream(new_data, rollover=8)
doc.add_periodic_callback(update, 150)
doc.add_root(p)
show(modify_doc)
# In[ ]:
### EXERCISE: starting with the above example, create your own streaming plot
# Bokeh column data sources also support a `patch` method that can be used to efficiently update subsets of data.
# ## Directory Format Apps and Templates
#
# Bokeh apps can also be defined with a directory format. This format affords the use of extra modules, data files, templates, theme files, and other features. The directory should contain a `main.py` which is the "entry point" for the app, but may contain extra parts:
# ```
# myapp
# |
# +---main.py
# +---server_lifecycle.py
# +---static
# +---theme.yaml
# +---templates
# +---index.html
# ```
# The [Directory Format](https://bokeh.pydata.org/en/latest/docs/user_guide/server.html#directory-format) section of the User's Guide has more information.
#
# See a complete sophisticated example at: https://github.com/bokeh/bokeh/tree/master/examples/app/dash
#
#
# ## Tips and Tricks
#
#
# * Real Python callbacks *require* a Bokeh server application. They cannot work with `output_file`, `components` or other functions that generate standalone output. Standalone content can only use `CustomJS` callbacks.
#
#
# * Try to update data sources "all at once" whenever possible, i.e. prefer this:
# ```python
# source.data = new_data_dict # GOOD
# ```
# rather then updating individual columns sequentially:
# ```python
# # LESS GOOD
# source.data['foo'] = new_foo_column
# source.data['bar'] = new_bar_column
# ```
# If the new columns are exactly the same length as the old ones, then updating sequentially will trigger extra updates and may result in bad visual effects.
# If the new columns are a different length than the old ones, then updating "all at once" is **mandatory**.
#
#
# * Each time a session is started, the Bokeh server runs the script (or `modify_doc`) function, and the code that is run ***must return completely new Bokeh objects every time***. It is not possible to share Bokeh objects between sessions. As a concrete example, this is what NOT to do:
# ```python
# source = ColumnDataSource(data) # VERY BAD - global outside modify_doc
#
# def modify_doc(doc):
# p = figure()
# p.circle('x', 'y', source=source)
# doc.add_root(p)
#
# ```
# The analogous situation would occur with a script if the script imports a global Bokeh object from a separate module (due to the way Python caches imports).
# # Next Section
# This is the last section of the main tutorial. To explore some extra topics, see the appendices:
#
# [A1 - Models and Primitives](A1%20-%20Models%20and%20Primitives.ipynb)
# [A2 - Visualizing Big Data with Datashader](A2%20-%20Visualizing%20Big%20Data%20with%20Datashader.ipynb)
# [A3 - High-Level Charting with Holoviews](A3%20-%20High-Level%20Charting%20with%20Holoviews.ipynb)
# [A4 - Additional Resources](A4%20-%20Additional%20Resources.ipynb)
#
# To go back to the overview, click [here](00%20-%20Introduction%20and%20Setup.ipynb).
# In[ ]: