Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

'Single Select' widget value #2618

Closed
softengash opened this issue Jul 26, 2015 · 18 comments
Closed

'Single Select' widget value #2618

softengash opened this issue Jul 26, 2015 · 18 comments

Comments

@softengash
Copy link

Hi,

I am working on 0.9.2 version. My main intention is to have multiple "single select" input widgets to be presented in an order such that options available in second widget onwards depends on the selection made in the previous widget.

I tried callback option and update a ColumnDataSource object and make its data as options for the other select widget. It doesn't seem to reflect. Second select always remain blank, as if it never gets the data.

Then I tried to get the selected value of the first widget by using '.value'. But it gives me the initial value of the widget only irrespective whatever I select from the options.

Not sure if it is an issue or am I missing anything. Kindly advise.

Thanks
Ashish

@birdsarah
Copy link
Member

Can you supply a code example. It really helps us narrow down the problem.

@softengash
Copy link
Author

Regarding callback, I realized my mistake where in I was calling a python function within the js snippet of the callback fn. Based on a value selected in one select, I need to do a lookup to get the options for the second select, if not a fn call, can i pass second CDS object in callback fn to be used for the lookup purpose?

If I don't use callback and rely on '.value', it is not returning the selected value.

Below are the two code examples.

Thanks in advance

--with callback ---

from bokeh.io import vform
from bokeh.models import Callback, ColumnDataSource
from bokeh.models.widgets import Select
from bokeh.plotting import figure, output_notebook, show
from bokeh.models.widgets import DataTable, DateFormatter, TableColumn
import pandas as pd
import sqlite3

bonn=sqlite3.connect('<Database>.db')
b = bonn.cursor()

output_notebook()

categories=["1","2","3"]

df = pd.DataFrame(columns=["1","2","3"], data=[(('a','b','c'), ('d','e','f'), ('g','h','i'))])
origin = ColumnDataSource(data=dict()).from_df(df)

source = ColumnDataSource(data=dict(sc=[]))
callback1 = Callback(args=dict(source=source), code="""
        var f = cb_obj.get('value')
        var c = origin[f]                                                     #how to pass second CDS obj origin ??
        source.data = dict(sc=[i for i in c[0]])
        source.trigger('change');
    """)

select1 = Select(title="Cat1", value=categories[0], options=categories, callback=callback1)
select2 = Select(title="Cat2", value="", options=source.data.values()[0])

layout = vform(select1,select2)

show(layout)

---- without callback ---

from bokeh.io import vform
from bokeh.models import Callback, ColumnDataSource
from bokeh.models.widgets import Select, Dropdown
from bokeh.plotting import figure, output_notebook, show
from bokeh.models.widgets import DataTable, DateFormatter, TableColumn
import pandas as pd
import sqlite3

bonn=sqlite3.connect('<Database>.db')
b = bonn.cursor()

output_notebook()

categories=["1","2","3"]

df = pd.DataFrame(columns=["1","2","3"], data=[(('a','b','c'), ('d','e','f'), ('g','h','i'))])
origin = ColumnDataSource(data=dict()).from_df(df)

select1 = Select(title="Cat1", value=categories[0], options=categories)

select2 = Select(title="Cat2", value="", options=[i for i in origin[select1.value][0]])

layout = vform(select1, select2)

show(layout)

@birdsarah
Copy link
Member

Yes. You can pass in as many source or other bokeh objects into the
callback as you would like.

In one of my examples - I pass in 50 ColumnDataSources to the callback!

@softengash
Copy link
Author

Value of source CDS is updated correctly within the callback function but it is NOT reflecting outside the function as i mentioned previously.

All examples I have seen have widget interaction data being used in plotting, here I am trying to use it in another widget instead. Is this okay?

Please help!

Here is the code.

from bokeh.io import vform
from bokeh.models.actions import Callback
from bokeh.models import ColumnDataSource
from bokeh.models.widgets import Select
from bokeh.plotting import figure, output_notebook, show

cats=["one","two","three"]

result={}

result['one'] = ['a','b','c']
result['two'] = ['d','e','f']
result['three'] = ['g','h','i']

origin = ColumnDataSource(result)

source = ColumnDataSource(data = dict())

callback = Callback(args=dict(source=source), code="""
        var f = cb_obj.get('value');
        var r = origin.get('data')[f];
        source.set('data', r);
        alert(source.get('data'));
        source.trigger('change');
    """)

callback.args["origin"] = origin

select1 = Select(title="Cat1", value=cats[0], options=cats, callback=callback)

select2 = Select(title="Cat2", value="", options=source.data.values())

layout = vform(select1, select2)

show(layout)

@bryevdv
Copy link
Member

bryevdv commented Jul 28, 2015

.data is a dict/object that maps names to columns/arrays of data. You are setting it to a single number.

@softengash
Copy link
Author

I am sorry, I didn't get it. Which line of code you are referring to?

If you are referring to javascript snippet, .data is being set to an Array. Alert in javascript confirms that.

@bryevdv
Copy link
Member

bryevdv commented Jul 28, 2015

Sorry, traveling and only on my phone can't really say much more now. But .data should not be an array. It should be a dictionary (js object) with string keys and array values.

@softengash
Copy link
Author

Changed the code, now .data is a dict with string key and an array value. Still the problem persists... source object is correctly updated within the JS snippet but it is not reflecting outside.

Kindly advise.

from bokeh.io import vform
from bokeh.models.actions import Callback
from bokeh.models import ColumnDataSource
from bokeh.models.widgets import Select
from bokeh.plotting import figure, output_notebook, show
import pandas as pd

output_notebook()


cats=["one","two","three"]

result={}

result['one'] = ['a','b','c']
result['two'] = ['d','e','f']
result['three'] = ['g','h','i']

origin = ColumnDataSource(result)

source = ColumnDataSource(data = dict(subcats=[]))

callback = Callback(args=dict(source=source), code="""
        var f = cb_obj.get('value');
        var r = origin.get('data')[f];
        var data = source.get('data');
        var k = data['subcats'];
        for (i = 0; i < r.length; i++) {
            k[i] = r[i]
        }
        alert(source.get('data')['subcats']);
        source.trigger('change');
    """)

callback.args["origin"] = origin

select1 = Select(title="Cat1", value=cats[0], options=cats, callback=callback)

select2 = Select(title="Cat2", value="", options=source.data['subcats'])

layout = vform(select1, select2)

show(layout)

@bryevdv
Copy link
Member

bryevdv commented Jul 29, 2015

ping @mattpap @birdsarah

@softengash
Copy link
Author

Hi @mattpap , @birdsarah ,

Please help!

Intention: To have multiple "single select" input widgets to be presented in an order such that options available in second widget onwards depends on the selection made in the previous widget. And finally present a data table based on these inputs.

Problem: source object is getting updated correctly within JS of select1 but not reflecting outside, due to which select2 remains empty.

---- code ---

from bokeh.io import vform
from bokeh.models.actions import Callback
from bokeh.models import ColumnDataSource
from bokeh.models.widgets import Select
from bokeh.plotting import figure, output_notebook, show
import pandas as pd

output_notebook()

cats=["one","two","three"]

result={}

result['one'] = ['a','b','c']
result['two'] = ['d','e','f']
result['three'] = ['g','h','i']

origin = ColumnDataSource(result)

source = ColumnDataSource(data = dict(subcats=[]))

callback = Callback(args=dict(source=source), code="""
var f = cb_obj.get('value');
var r = origin.get('data')[f];
var data = source.get('data');
var k = data['subcats'];
for (i = 0; i < r.length; i++) {
k[i] = r[i]
}
alert(source.get('data')['subcats']);
source.trigger('change');
""")

callback.args["origin"] = origin

select1 = Select(title="Cat1", value=cats[0], options=cats, callback=callback)

select2 = Select(title="Cat2", value="", options=source.data['subcats'])

layout = vform(select1, select2)

show(layout)

@birdsarah
Copy link
Member

I don't think this is possible. I don't think the options for a Select widget are "powered" by a data source. All you are doing is setting the options to a list (source.data['subcats'] is a list of values) you are not making a link between the Select widget and a data source.

@fpliger
Copy link
Contributor

fpliger commented Jul 29, 2015

@softengash you should change the Select widget options field directly instead of using a data source, for the reasone @birdsarah mentioned. For instance you can use:

s2.set('options', k);
s2.trigger('change');

on your callback if you define s2 like:

callback.args["s2"] = select2

@softengash
Copy link
Author

I did as suggested and it finally worked!! Thank you :) @fpliger

So how would I get the selected value of select1 outside the JS code to do a data lookup in python?

'select1.value' in python returns None!!

Kindly advise.

@birdsarah
Copy link
Member

Have you tried select1.get('value')?

@bryevdv
Copy link
Member

bryevdv commented Jul 29, 2015

To get the value back from a Bokeh widget to python code you will have to use a Bokeh Server. If you just need widgets in the Jupyter/IPython notebook, you might looks at some of the examples that use IPython interactors (look under examples/plotting/notebook, e.g.), which would be simpler than using a Bokeh server.

@softengash
Copy link
Author

Thank you @bryevdv

@damianavila damianavila added this to the 0.11.0 milestone Nov 18, 2015
@paulmicheletty
Copy link

@softengash do you have a final example of this working? I still can't seem to put it all together. Thanks!

@softengash
Copy link
Author

@paulmicheletty

callback = Callback(args=dict(source=source), code="""
var f = cb_obj.get('value');
// your manipulations with f to get new values, say nvalues
s2.set('options', nvalues);
s2.trigger('change');
""")
callback.args["s2"] = s2

s1 = Select(title="Select1", value='initial value in your dropdown', options='list of values of your dropdown', callback=callback)
s2 = Select(title="Select2", value='initial value in your dropdown'))

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

6 participants