0

this is my first experience with bokeh. I am using windows 7, Pycharm as IDE, and python 3.7. I am trying to make a form with 4 TextInput to get values for mean, variance, number of rows and number of columns from user, with a Button that when clicked generate a dataframe of random numbers with normal distribution with input mean and variance with the defined input size. I have made the form by following the adding widgets link, and have written the dataframe generator. But I have difficulty binding it to the button. In order to make sure that I am correctly handling the event, I have added another TextInput just to show the value of mean TextInput when the button is clicked. The code is as follows:

from bokeh.models.widgets import TextInput, Button, Div
from bokeh.layouts import layout, column, row
from bokeh.io import curdoc ## to assign callback to widget

from bokeh import events    ## for event handling
from bokeh.models import CustomJS

import numpy as np
import pandas as pd

text_input_mean = TextInput(value="0.0", title="Enter mean:")
text_input_vaiance = TextInput(value="0.0", title="Enter variance:")
text_input_rows = TextInput(value="5", title="Enter num rows:")
text_input_columns = TextInput(value="5", title="Enter num columns:")

button = Button(label = "Generate Dataframe", button_type = "success")

text_output = TextInput(title = 'Python result is shown here: ')

div = Div(text="""Making a form with bokeh mainly to handle events.""",
width=500, height=50)

layout = column(div, row(text_input_mean, text_input_vaiance), row(text_input_rows, text_input_columns),
                button, text_output)
show(layout)

## Events with no attributes
# button.js_on_event(events.ButtonClick, button_click_handler) # Button click

def my_text_input_handler(attr, old, new):
    print("Previous label: " + old)
    print("Updated label: " + new)
text_input_mean.on_change("value", my_text_input_handler)

def button_click_handler():
    text_output.__setattr__('value', str(text_input_mean.value))
    #text_output.value = str(text_input_mean.value)



button.on_click(button_click_handler)
# button.on_click(generate_normal_df)
# curdoc().add_root(layout)

def generate_normal_df(mean, var, x, y):
    mean = text_input_mean.value
    variance = text_input_vaiance.value
    row_num = x
    col_num = y
    return pd.DataFrame(np.random.normal(loc = mean, scale = variance, size=(row_num, col_num)))

The form looks as below:enter image description here

When the button is clicked nothing happens! I looked into console as well, but there is nothing there either. I have used the method "my_text_input_handler" in the same link to see if I change the value of text_input_mean something happens, but there is no change either. I tried another way to by following this link, that's why is part of my code you can see:

button.on_click(generate_normal_df)
curdoc().add_root(layout)

which I have later commented. But when I use these two lines of code I get the following error:

enter image description here

Also the line:

button.on_click(button_click_handler)

is causing another error in my IDE. When I use button_click_handler() I get the following error:

File "C:\Users\E17538\anaconda3\lib\site-packages\bokeh\model.py", line 609, in _attach_document raise RuntimeError("Models must be owned by only a single document, %r is already in a doc" % (self)) RuntimeError: Models must be owned by only a single document, WidgetBox(id='1009', ...) is already in a doc

When I use it without (), I get again the same error. I appreciate if you guide me how I can bind the handlers to my widgets. Thank you very much.

E. Erfan
  • 1,239
  • 19
  • 37

1 Answers1

1

There are a few problems with your code. The first is that you should generally not use both show and curdoc().add_root(...) at the same time. The show function is for displaying standalone Bokeh content, i.e. only static HTML and JS, with no Bokeh server. However, curdoc().add_root is typically used in the context of a Bokeh server application. Both functions add the values they are passed to a new Bokeh Document, which is the proximate cause of the error (models can only belong to one Document, and you've added them to two). The correct remedy in this case is to remove the call to show.

Additionally, on_click expects a callback function that takes zero required parameters. But you are passing generate_normal_df which has four required positional parameters. This cannot work (when the button is clicked, Bokeh has no way of knowing what parameters to pass). If you want to use generate_normal_df you would need to wrap it with a lambda or use functools.partial to bake in some fixed parameter values, and pass the result.

bigreddot
  • 33,642
  • 5
  • 69
  • 122
  • tnx, I see your point now. But in that case why does button_click_handler doesn't work as I bind it to .on_click()? It doesn't have any arguments, and the only thing I it should do is to change the value of TextInput. – E. Erfan Jan 29 '19 at 08:43