Dynamically adding widgets kill Plot interactivity

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()

I can confirm the culprit is using tabs (or perhaps Panel). The first set of code is a much more simplified version of what I posted earlier, but still illustrating the issue. The second set of code below is exactly same, except I do not use tabs, and this seems to work ok. Any chance someone could confirm this is a bug?

Bad:

from bokeh.layouts import layout, row
from bokeh.models import ColumnDataSource
from bokeh.models.widgets import Select, Button, Tabs, Panel
from bokeh.plotting import figure
from bokeh.io import curdoc

source = ColumnDataSource(data={‘x’: [0, 1, 2, 3, 4],
‘y’: [0, 1, 4, 9, 16]})

def update():
pass

def button_add_selector_row():
layout_query.children.append(AddSelectorRow().row)

class AddSelectorRow:
def init(self):
self.select_category = Select(value=“option1”, options=[“option1”, “option2”])
self.row = row([self.select_category])

set up layout

plot = figure(plot_width=1000, plot_height=400)
plot.line(‘x’, ‘y’, source=source)
layout_data = layout([[plot]])

Set up initial buttons

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_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)

update()


Good:

from bokeh.layouts import layout, row
from bokeh.models import ColumnDataSource
from bokeh.models.widgets import Select, Button, Tabs, Panel
from bokeh.plotting import figure
from bokeh.io import curdoc

source = ColumnDataSource(data={‘x’: [0, 1, 2, 3, 4],
‘y’: [0, 1, 4, 9, 16]})

def update():
pass

def button_add_selector_row():
layout_query.children.append(AddSelectorRow().row)

class AddSelectorRow:
def init(self):
self.select_category = Select(value=“option1”, options=[“option1”, “option2”])
self.row = row([self.select_category])

set up layout

plot = figure(plot_width=1000, plot_height=400)
plot.line(‘x’, ‘y’, source=source)

Set up initial buttons

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_add_selector_button], [plot]])

curdoc().add_root(layout_query)

update()


<details class='elided'>
<summary title='Show trimmed content'>&#183;&#183;&#183;</summary>

On Tuesday, April 25, 2017 at 12:31:24 PM UTC-5, [email protected] wrote:
> 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](http://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()
> ```

</details>

Hi

There are definitely some problems with Tabs/Panels. I am not sure if any of the existing issues covers this particular case or not. I'd encourage a quick GH search and a new issue if nothing obviously duplicate shows up.

Thanks,

Bryan

···

On Apr 26, 2017, at 20:46, [email protected] wrote:

I can confirm the culprit is using tabs (or perhaps Panel). The first set of code is a much more simplified version of what I posted earlier, but still illustrating the issue. The second set of code below is exactly same, except I do not use tabs, and this seems to work ok. Any chance someone could confirm this is a bug?

Bad:
from bokeh.layouts import layout, row
from bokeh.models import ColumnDataSource
from bokeh.models.widgets import Select, Button, Tabs, Panel
from bokeh.plotting import figure
from bokeh.io import curdoc

source = ColumnDataSource(data={'x': [0, 1, 2, 3, 4],
                                'y': [0, 1, 4, 9, 16]})

def update():
    pass

def button_add_selector_row():
    layout_query.children.append(AddSelectorRow().row)

class AddSelectorRow:
    def __init__(self):
        self.select_category = Select(value="option1", options=["option1", "option2"])
        self.row = row([self.select_category])

# set up layout
plot = figure(plot_width=1000, plot_height=400)
plot.line('x', 'y', source=source)
layout_data = layout([[plot]])

# Set up initial buttons
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_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)

update()

Good:
from bokeh.layouts import layout, row
from bokeh.models import ColumnDataSource
from bokeh.models.widgets import Select, Button, Tabs, Panel
from bokeh.plotting import figure
from bokeh.io import curdoc

source = ColumnDataSource(data={'x': [0, 1, 2, 3, 4],
                                'y': [0, 1, 4, 9, 16]})

def update():
    pass

def button_add_selector_row():
    layout_query.children.append(AddSelectorRow().row)

class AddSelectorRow:
    def __init__(self):
        self.select_category = Select(value="option1", options=["option1", "option2"])
        self.row = row([self.select_category])

# set up layout
plot = figure(plot_width=1000, plot_height=400)
plot.line('x', 'y', source=source)

# Set up initial buttons
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_add_selector_button], [plot]])

curdoc().add_root(layout_query)

update()

On Tuesday, April 25, 2017 at 12:31:24 PM UTC-5, [email protected] wrote:
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()

--
You received this message because you are subscribed to the Google Groups "Bokeh Discussion - Public" group.
To unsubscribe from this group and stop receiving emails from it, send an email to [email protected].
To post to this group, send email to [email protected].
To view this discussion on the web visit https://groups.google.com/a/continuum.io/d/msgid/bokeh/5f965baa-5464-43f4-96d0-66696af410ed%40continuum.io.
For more options, visit https://groups.google.com/a/continuum.io/d/optout.

Thank you for your response Bryan, and being so active with the online community. I did find an open issue and I replied to it:

Plot becomes unresponsive when button widget is appended to layout #5856

···

On Friday, April 28, 2017 at 10:25:02 AM UTC-5, Bryan Van de ven wrote:

Hi

There are definitely some problems with Tabs/Panels. I am not sure if any of the existing issues covers this particular case or not. I’d encourage a quick GH search and a new issue if nothing obviously duplicate shows up.

Thanks,

Bryan

On Apr 26, 2017, at 20:46, [email protected] wrote:

I can confirm the culprit is using tabs (or perhaps Panel). The first set of code is a much more simplified version of what I posted earlier, but still illustrating the issue. The second set of code below is exactly same, except I do not use tabs, and this seems to work ok. Any chance someone could confirm this is a bug?

Bad:

from bokeh.layouts import layout, row

from bokeh.models import ColumnDataSource

from bokeh.models.widgets import Select, Button, Tabs, Panel

from bokeh.plotting import figure

from bokeh.io import curdoc

source = ColumnDataSource(data={‘x’: [0, 1, 2, 3, 4],

                            'y': [0, 1, 4, 9, 16]})

def update():

pass

def button_add_selector_row():

layout_query.children.append(AddSelectorRow().row)

class AddSelectorRow:

def __init__(self):
    self.select_category = Select(value="option1", options=["option1", "option2"])
    self.row = row([self.select_category])

set up layout

plot = figure(plot_width=1000, plot_height=400)

plot.line(‘x’, ‘y’, source=source)

layout_data = layout([[plot]])

Set up initial buttons

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_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)

update()

Good:

from bokeh.layouts import layout, row

from bokeh.models import ColumnDataSource

from bokeh.models.widgets import Select, Button, Tabs, Panel

from bokeh.plotting import figure

from bokeh.io import curdoc

source = ColumnDataSource(data={‘x’: [0, 1, 2, 3, 4],

                            'y': [0, 1, 4, 9, 16]})

def update():

pass

def button_add_selector_row():

layout_query.children.append(AddSelectorRow().row)

class AddSelectorRow:

def __init__(self):
    self.select_category = Select(value="option1", options=["option1", "option2"])
    self.row = row([self.select_category])

set up layout

plot = figure(plot_width=1000, plot_height=400)

plot.line(‘x’, ‘y’, source=source)

Set up initial buttons

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_add_selector_button], [plot]])

curdoc().add_root(layout_query)

update()

On Tuesday, April 25, 2017 at 12:31:24 PM UTC-5, [email protected] wrote:

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()


You received this message because you are subscribed to the Google Groups “Bokeh Discussion - Public” group.

To unsubscribe from this group and stop receiving emails from it, send an email to [email protected].

To post to this group, send email to [email protected].

To view this discussion on the web visit https://groups.google.com/a/continuum.io/d/msgid/bokeh/5f965baa-5464-43f4-96d0-66696af410ed%40continuum.io.

For more options, visit https://groups.google.com/a/continuum.io/d/optout.