Callback needs to be reassigned when argument changes?

Hi,
I’m using a multiselect that I want to repopulate anytime I click on a radio button. Along with this, I have a text input associated to a CustomJS callback which filters the choices in the multiselect.

Here is the code:

userList = [('a', 'a'), ('b', 'b'), ('c', 'c')]
folderList = [('d', 'd'), ('e', 'e'), ('f', 'f')]
options = userList

s = MultiSelect(options=options, size=4, width=1000)
ti = TextInput(placeholder='Enter reference')
ti.callback = CustomJS(args=dict(x=options, s=s),
                       code="s.options = x.filter(i => i[0].toLowerCase().includes(cb_obj.value.toLowerCase()));console.log(x);")

def populateDropdown(attr, old, new):
  nonlocal options
  if new == 0:# Users
    options = userList

  else: # Folders
    options = folderList
  s.options = options

usersFoldersChoice = RadioButtonGroup(labels=[“Users”, “Folders”])
usersFoldersChoice.on_change(‘active’, populateDropdown)


The callback populateDropdown works correctly and each time I switch for "Users" of "Folders", the multiselect is correctly modified. However, the CustomJS callback associated to the TextInput ti dos not work so well. The "console.log(x)" always display the same list, which is here "userList", even if populateDropdown changes the nonlocal variable options. To make the log display folderList, I have to put the "ti.callback" assignment in the callback populateDropdown, as if I had to reassign it each time "options" changes.

Anyone could explain such a behaviour?

Hi,

Might be a bug? Or maybe not. It's hard to say without investigating, which means actually running code. If you can provide a complete minimal reproducer, that I can run without modification, I'm happy to take a closer look.

Thanks,

Bryan

···

On Oct 25, 2018, at 00:38, nicoowr <[email protected]> wrote:

Hi,
I'm using a multiselect that I want to repopulate anytime I click on a radio button. Along with this, I have a text input associated to a CustomJS callback which filters the choices in the multiselect.
Here is the code:
userList = [('a', 'a'), ('b', 'b'), ('c', 'c')]
folderList = [('d', 'd'), ('e', 'e'), ('f', 'f')]
options = userList

s = MultiSelect(options=options, size=4, width=1000)
ti = TextInput(placeholder='Enter reference')
ti.callback = CustomJS(args=dict(x=options, s=s),
                       code="s.options = x.filter(i => i[0].toLowerCase().includes(cb_obj.value.toLowerCase()));console.log(x);")

def populateDropdown(attr, old, new):
  nonlocal options
  if new == 0:# Users
    options = userList

  else: # Folders
    options = folderList
  s.options = options

usersFoldersChoice = RadioButtonGroup(labels=["Users", "Folders"])
usersFoldersChoice.on_change('active', populateDropdown)

The callback populateDropdown works correctly and each time I switch for "Users" of "Folders", the multiselect is correctly modified. However, the CustomJS callback associated to the TextInput ti dos not work so well. The "console.log(x)" always display the same list, which is here "userList", even if populateDropdown changes the nonlocal variable options. To make the log display folderList, I have to put the "ti.callback" assignment in the callback populateDropdown, as if I had to reassign it each time "options" changes.

Anyone could explain such a behaviour?

--
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/28dd4beb-a8c7-469a-905d-389ef8b31aeb%40continuum.io\.
For more options, visit https://groups.google.com/a/continuum.io/d/optout\.

Hi,
Here is a minimal reproducer to be launched in a bokeh server. The code is:

from bokeh.models import TextInput, CustomJS
from bokeh.models.widgets import MultiSelect, RadioButtonGroup
from bokeh.io import curdoc
from bokeh.layouts import column

userList = [('a', 'a'), ('b', 'b'), ('c', 'c')]
folderList = [('d', 'd'), ('e', 'e'), ('f', 'f')]
options = userList

s = MultiSelect(options=options, size=4, width=1000)
ti = TextInput(placeholder='Enter reference')
ti.callback = CustomJS(args=dict(x=options, s=s),
                       code="s.options = x.filter(i => i[0].toLowerCase().includes(cb_obj.value.toLowerCase()));console.log(x);")

def populateDropdown(attr, old, new):
  global options
  if new == 0:# Users
    options = userList

  else: # Folders
    options = folderList
  s.options = options
  ti.callback = CustomJS(args=dict(x=options, s=s),
                         code="s.options = x.filter(i => i[0].toLowerCase().includes(cb_obj.value.toLowerCase()));console.log(x);")

usersFoldersChoice = RadioButtonGroup(labels=["Users", "Folders"], active=0)
usersFoldersChoice.on_change('active', populateDropdown)

layout = column(usersFoldersChoice, ti, s)

curdoc().add_root(layout)

Enter something in the textinput, the browser console will print the options with userList. If you click on Folders and enter again something in the textinput, the browser will show the updated options with folderList. However, if you comment the “ti.callback” lines inside the callback populateDropdown, the console will always display userList.

Based on other observations, I think at launch, bokeh reads the CustomJS and assigns the arguments with their value at this time. Then they don’t get updated anymore except if you reassign the callback. Does this make sense ?

example.zip (927 Bytes)

···

Le jeudi 25 octobre 2018 17:30:14 UTC+2, Bryan Van de ven a écrit :

Hi,

Might be a bug? Or maybe not. It’s hard to say without investigating, which means actually running code. If you can provide a complete minimal reproducer, that I can run without modification, I’m happy to take a closer look.

Thanks,

Bryan

On Oct 25, 2018, at 00:38, nicoowr [email protected] wrote:

Hi,
I’m using a multiselect that I want to repopulate anytime I click on a radio button. Along with this, I have a text input associated to a CustomJS callback which filters the choices in the multiselect.
Here is the code:

userList = [(‘a’, ‘a’), (‘b’, ‘b’), (‘c’, ‘c’)]

folderList = [(‘d’, ‘d’), (‘e’, ‘e’), (‘f’, ‘f’)]

options = userList

s = MultiSelect(options=options, size=4, width=1000)

ti = TextInput(placeholder=‘Enter reference’)

ti.callback = CustomJS(args=dict(x=options, s=s),

                   code="s.options = x.filter(i => i[0].toLowerCase().includes(cb_obj.value.toLowerCase()));console.log(x);")

def populateDropdown(attr, old, new):

nonlocal options

if new == 0:# Users

options = userList

else: # Folders

options = folderList

s.options = options

usersFoldersChoice = RadioButtonGroup(labels=[“Users”, “Folders”])

usersFoldersChoice.on_change(‘active’, populateDropdown)

The callback populateDropdown works correctly and each time I switch for “Users” of “Folders”, the multiselect is correctly modified. However, the CustomJS callback associated to the TextInput ti dos not work so well. The “console.log(x)” always display the same list, which is here “userList”, even if populateDropdown changes the nonlocal variable options. To make the log display folderList, I have to put the “ti.callback” assignment in the callback populateDropdown, as if I had to reassign it each time “options” changes.

Anyone could explain such a behaviour?


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/28dd4beb-a8c7-469a-905d-389ef8b31aeb%40continuum.io.

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