I’m having an issue using Bokeh server such I lose the ability to interact with a plot after editing the document layout (i.e., dynamically adding widgets).
The plot works as expected initially, but adding a new row of widgets kills the plot. The work around I’ve done is to create a new figure and replace the original figure in the layout with layout.children[instance] = row(figure_name) with another button that runs this code. However, this doesn’t appear to be stable with mixed performance on different browsers / OS’s.
Is this due to a bug or the way I’ve created classes of widgets?
Is there a preferred way of adding a new widget or group of widgets to a curdoc()?
The code below is long, but substantially shorter than the original. I can share, but it involves querying a local SQL DB and a ton of other python code to go with it. However, I think the isolated code below illustrates the potential bug.
Thanks so much for any help!!
Dan
Command line output after initializing server
2017-04-25 12:14:26,264 Starting Bokeh server version 0.12.5
2017-04-25 12:14:26,269 Starting Bokeh server on port 5006 with applications at paths [’/adding_widget_bug’]
2017-04-25 12:14:26,269 Starting Bokeh server with process id: 6481
2017-04-25 12:14:26,598 E-1001 (BAD_COLUMN_NAME): Glyph refers to nonexistent column name: x, y [renderer: GlyphRenderer(id=‘4d062f00-62b4-4054-9174-c215f64212b9’, …)]
2017-04-25 12:14:26,604 200 GET /adding_widget_bug (127.0.0.1) 175.50ms
2017-04-25 12:14:27,116 WebSocket connection opened
2017-04-25 12:14:27,116 ServerConnection created
No command line info when adding a widget row
Command line after clicking update in second tab only after adding a widget row
2017-04-25 12:15:33,085 error handling message Message ‘PATCH-DOC’ (revision 1): RuntimeError(‘Cannot apply patch to 981ab2bd-a8e7-4805-8f5f-3713d950cc5c which is not in the document’,)
2017-04-25 12:15:33,086 error handling message Message ‘PATCH-DOC’ (revision 1): RuntimeError(‘Cannot apply patch to 981ab2bd-a8e7-4805-8f5f-3713d950cc5c which is not in the document’,)
from bokeh.layouts import layout, column, row
from bokeh.models import ColumnDataSource
from bokeh.models.widgets import Select, Paragraph, Button, Tabs, Panel, PreText, TableColumn, DataTable
from bokeh.plotting import figure
from bokeh.io import curdoc
# Initialize variables
source = ColumnDataSource(data=dict())
query_row_count = 0
query_row = {}
# Categories map of dropdown values, SQL column, and SQL table
selector_categories = {'Category1': {'var_name': 'institutional_roi', 'table': 'DVHs'},
'Category2': {'var_name': 'physician_roi', 'table': 'DVHs'}}
def update():
source.data = {'x': [0, 1, 2, 3, 4],
'y': [0*(query_row_count + 1),
1*(query_row_count + 1),
4*(query_row_count + 1),
9*(query_row_count + 1),
16*(query_row_count + 1)]}
query.text = 'Plans: ' + str(query_row_count)
plot_new = figure(plot_width=1000, plot_height=400)
plot_new.line('x', 'y', source=source)
layout_data.children[1] = row(plot_new)
def button_add_selector_row():
global query_row_count
query_row[query_row_count] = AddSelectorRow()
layout_query.children.append(query_row[query_row_count].row)
query_row_count += 1
def button_del_row():
global query_row_count
del(layout_query.children[query_row_count])
query_row_count -= 1
class AddSelectorRow:
def __init__(self):
# Plans Tab Widgets
# Category Dropdown
self.category_options = selector_categories.keys()
self.category_options.sort()
self.select_category = Select(value=self.category_options[0], options=self.category_options)
self.select_category.on_change('value', self.update_selector_values)
# Value Dropdown
self.sql_table = selector_categories[self.select_category.value]['table']
self.var_name = selector_categories[self.select_category.value]['var_name']
self.values = ['value1', 'value2']
self.select_value = Select(value=self.values[0], options=self.values)
self.add_selector_button = Button(label="Add Selector", button_type="default", width=100)
self.add_selector_button.on_click(button_add_selector_row)
self.delete_last_row = Button(label="Delete", button_type="warning", width=100)
self.delete_last_row.on_click(button_del_row)
self.row = row([self.select_category,
self.select_value,
self.add_selector_button,
self.delete_last_row])
def update_selector_values(self, attrname, old, new):
new_options = ['new option1', 'new option 2']
self.select_value.options = new_options
self.select_value.value = new_options[0]
def initialize_source_data():
source.data = {'x': [0, 1, 2, 3, 4],
'y': [0, 1, 4, 9, 16]}
# set up layout
plot = figure(plot_width=1000, plot_height=400)
plot.line('x', 'y', source=source)
update_query_button = Button(label="Update", button_type="success", width=100)
update_query_button.on_click(update)
query = Paragraph(text="This will contain the final SQL Query", width=1000)
# Set up DataTable
columns = [TableColumn(field="x", title="x", width=175),
TableColumn(field="y", title="x^2")]
data_table = DataTable(source=source, columns=columns, width=1000, selectable=True)
widgets = column(query, update_query_button)
layout_data = layout([[widgets], [plot], [data_table]])
# Set up initial buttons
main_pre_text = PreText(text="Add selectors and sliders to design your query", width=500)
main_add_selector_button = Button(label="Add Selector", button_type="default", width=400)
main_add_selector_button.on_click(button_add_selector_row)
layout_query = layout([[main_pre_text, main_add_selector_button]])
# Generate tabs
tab_query = Panel(child=layout_query, title="Query")
tab_data = Panel(child=layout_data, title="Data")
tabs = Tabs(tabs=[tab_query, tab_data])
curdoc().add_root(tabs)
curdoc().title = "Live Free or DICOM"
update()