I have three multichoice widgets (w1, w2, w3) and I’m trying to have their options and values depend on each other when they are running pn.Row(w1, w2, w3).show().
Can someone please suggest a direction how to go about this. Relatively newbie here.
HI
Here are a few tutorials that may help you:
https://panel.holoviz.org/user_guide/Links.html
https://param.holoviz.org/user_guide/Dependencies_and_Watchers.html?highlight=watch
And here i am writing a really briefly example to you with link function:
# import libary
import pandas as pd
import panel as pn
import random
# if you are not vscode, you can delte comms = 'vscode'
pn.extension(comms = 'vscode')
# Generate Some data
df = pd.DataFrame({'a':[random.randint(1,3) for i in range(10)],
'b':[random.randint(3,6) for i in range(10)],
'c':[random.randint(1,2) for i in range(10)]})
df['result'] = df['a'] * df['b'] * df['c']
# define widgets
W1 = pn.widgets.Select(options = np.sort(df['a'].unique()).tolist())
W2 = pn.widgets.Select(options = np.sort(df['b'].unique()).tolist())
W3 = pn.widgets.Select(options = np.sort(df['c'].unique()).tolist())
result = pn.pane.Str()
# define callbacks wiht watch function
def callback_W12W2(target, event):
"""
The target here is the W2 widget, and the event is the change in W1
"""
new_options = np.sort(df[(df['a']==event.new)]['b'].unique()).tolist()
target.options = new_options
# link the widgets, the 'value' is the value of the widget W1, and it is watched, when it changes, the callback_W12W2 is called
W1.link(W2, callbacks={'value': callback_W12W2})
def callback_W22W3(target, event):
new_options = np.sort(df[(df['a']==W1.value) & (df['b']==event.new)]['c'].unique()).tolist()
target.options = new_options
W2.link(W3, callbacks={'value': callback_W22W3})
def callback_result(target, event):
new_options = df[(df['a']==W1.value) & (df['b']==W2.value) & (df['c']==W3.value)]['result'].values[0]
# the pn.pane.Str is a widget that can display text, the data is stored in the .object attribute
target.object = new_options
W3.link(result, callbacks={'value': callback_result})
pn.Column(
pn.Row(W1,W2,W3),
result,
df
).show()
Kind regards
Victor
Thank you for taking the time to write this . Much much appreciated. I’m reading through the code and trying to understand it. Will get back here when I’ve understood or if I have a followup question.
Hi Victor, I’m struggling to fully understand it. I’ve replaced the select widgets with multichoice widget as it fits my case better. I’m getting an error as W1.value is a list and it raises TypeError: unhashable type: ‘list’. For every option ‘selected’ in W1, there are values to be populated in W2. For every option ‘selected’ in W2, there are options to be populated in W1 and W3. And for every option in W3 there are values to be populated in W2. I can access those options by indexing the dictionary etc containing these different correspondences. This figure represents the relationship between the options of the three widgets.
I’m trying myself to figure it but if you have any feedback, i’d appreciate. Thanks.
Hi rys
i think the error may coming from your call back function, If you get me your code, i would be batter for me to point out it
Here is my best guess where is your bug coming form:
W2_options = {'A0': ['B0'],
'A1': ['B0', 'B1'],
'A2': ['B0', 'B1', 'B2'],
'A3': ['B0', 'B1', 'B2', 'B3'],}
W2_options
W1 = pn.widgets.MultiChoice(name='W1', options=W1_options)
W2 = pn.widgets.MultiChoice(name='W2', options=[np.NaN])
def callback_W12W2(target, event):
new_options = W2_options[event.new]# !!!!!!!!!!!!!! here may your bug coming from
#you should not give event.new here, cause it is a list( in previous example, select is always given a value, and your muti-selecter is given a list), to find your values in dic, you need a single value, you may need to re-think about it what you actually want to achieve here.
# to make it works, try this: new_options = W2_options[event.new[0]]
target.options = new_options
W1.link(W2, callbacks={'value': callback_W12W2})
pn.Row(W1,W2,height = 300)
Kind regards
Victor
Hi Victor, here is the code but I haven’t manage to do the call back functions with this.
import panel as pn
import networkx as nx
import hvplot.networkx as hvnx
ccc = ['a', 'b', 'c']
ddd = ['p', 'q', 'r']
yyy = ['x', 'y', 'z']
edge_list_tuples = [('a', 'q'), ('b', 'p'), ('b', 'q'), ('c', 'r'), ('p', 'y'), ('r', 'y'), ('q', 'z'), ('r', 'x'), ('c', 'q'), ('p', 'x')]
G = nx.Graph()
G.add_nodes_from(ccc, Wgt=1)
G.add_nodes_from(ddd, Wgt=2)
G.add_nodes_from(yyy, Wgt=3)
G.add_edges_from(edge_list_tuples)
pos = nx.multipartite_layout(G, subset_key="Wgt")
hvnx.draw(G, pos, with_labels=True) # just for visualization
W1 = pn.widgets.MultiChoice(name='W1', options=ccc)
W2 = pn.widgets.MultiChoice(name='W2', options=ddd)
W3 = pn.widgets.MultiChoice(name='W3', options=yyy)
# no idea from here
based on this graph, say if ‘b’ is selected from W1, ‘p’ and ‘q’ should be selected in W2 and in turn, ‘x’,‘y’,‘z’ should be selected in W3. This is the effect i’m trying to get at.
I think it may be easier to implement the selection rather than changing options every time other widgets are changed. I’d like this same behavior for all widgets. From any widget, if i select an option, other connected options from other widgets are selected.
I hope this is easier to understand. Please have a look.
Hi rys
Check this
# import libary
import pandas as pd
import panel as pn
import random
# if you are not vscode, you can delte comms = 'vscode'
pn.extension(comms = 'vscode')
# Generate Some data
W1_options = {'a':['q'],'b':['p','q'],'c':['q','r']}
W2_options = {'p':['x','y'],'q':['z'],'r':['x','y']}
W3_options = ['z']
# define widgets
W1 = pn.widgets.Select(options = list(W1_options.keys()))
W2 = pn.widgets.Select(options = ['q'])
W3 = pn.widgets.Select(options = ['z'])
# define callbacks wiht watch function
def callback_W12W2(target, event):
"""
The target here is the W2 widget, and the event is the change in W1
"""
target.options = W1_options[event.new]
# link the widgets, the 'value' is the value of the widget W1, and it is watched, when it changes, the callback_W12W2 is called
W1.link(W2, callbacks={'value': callback_W12W2})
def callback_W22W3(target, event):
target.options = W2_options[event.new]
W2.link(W3, callbacks={'value': callback_W22W3})
pn.Row(W1,W2,W3).show()
For more explanation you need to have a look at this:
https://panel.holoviz.org/user_guide/Links.html
Kind regadrs
Victor