Я не думаю, что ваш пример должен даже создавать документ, и ваши текстовые входы, и модели с множественным выбором имеют один и тот же идентификатор, из-за чего может быть испорчено отображение вкладок.
Мое решение похоже на решение HYRY, но с более общей функцией для обмена атрибутами, используя две разные вещи:
model.properties_with_values ()
Может использоваться с любой моделью боке и возвращает словарьвсе пары атрибут: значение модели.В ipython в основном полезно исследовать объекты bokeh и отлаживать
Document.select ({'type': model_type})
Генератор всех виджетов нужного типав документе
Затем я просто отфильтровываю виджеты, которые не разделяют те же теги, что и виджет ввода, что позволит избежать "синхронизации" других входов / множественного выбора, не сгенерированных с помощью box_maker ().Я использую теги, потому что разные модели не могут иметь одно и то же имя.
Когда вы изменяете значение TextInput, это изменит связанный Multiselect в функции обновления, но также изменит все другие TextInputs и запустит их обновление вТочно так же.Таким образом, каждый триггер Input обновляется один раз и меняет параметры соответствующего им мультиселекта (и не умножает каждый раз, потому что это обратный вызов on_change, если вы задаете то же значение для нового входа, который он не запускает).
Для Multiselect первый триггер обновления выполнит свою работу, но, поскольку он изменил значения другого Multiselect, он все еще будет запускаться столько раз, сколько существует виджетов Multiselect.
from bokeh.io import curdoc
from bokeh.layouts import widgetbox
from bokeh.models import TextInput, MultiSelect
from bokeh.models.widgets import Panel, Tabs
import pandas as pd
from functools import partial
df = pd.DataFrame(["apples", "oranges", "grapes"], columns=["fruits"])
def sync_attr(widget):
prop = widget.properties_with_values() # dictionary of attr:val pairs of the input widget
for elem in curdoc().select({'type':type(widget)}): # loop over widgets of the same type
if (elem!=widget) and (elem.tags==widget.tags): # filter out input widget and those with different tags
for key in prop: # loop over attributes
setattr(elem,key,prop[key]) # copy input properties
def update(attr,old,new,widget,other_widget):
print("\ndf['fruits']: {}".format(list(df['fruits'])))
print("{} : {} changed: Old [ {} ] -> New [ {} ]".format(str(widget),attr, old, new))
if type(widget)==TextInput:
col_data = list(df[df['fruits'].str.contains(new)]['fruits'])
print("col_date: {}".format(col_data))
other_widget.update(options = sorted(list(col_data)))
sync_attr(widget)
def box_maker():
multiselect = multiselect = MultiSelect(title = 'multiselect',tags=['in_box'],value = [],options = list(df["fruits"]))
input_box = TextInput(title = 'input',tags=['in_box'],value='Enter you choice')
multiselect.on_change('value',partial(update,widget=multiselect,other_widget=input_box))
input_box.on_change('value',partial(update,widget=input_box,other_widget=multiselect))
return widgetbox(input_box, multiselect)
box_list = [box_maker() for i in range(2)]
tabs = [Panel(child=box,title="Panel{}".format(i)) for i,box in enumerate(box_list)]
tabs = Tabs(tabs=tabs)
curdoc().add_root(tabs)
Обратите внимание, что выделениепараметры в множественном выборе могут выглядеть не согласованно, но это только кажется визуальным, поскольку значения / параметры каждого из них изменяются правильно.
Но если вы не особенно привязаны к внешнему виду макета, когда вы помещаете виджеты внутрьпанели, вы можете просто поместить один вход и множественный выбор снаружи, и написать их обратные вызовы, чтобы иметь дело с тем, что будет на разных панелях.