'How can I force two jupyter sliders to interact with one another (non-trivially)? Is "tag" available for handler?
I want to create two ipywidget sliders, say one with value x, the other with value 1-x. When I change one slider, the other one should be automatically changed acccordingly. I am trying to use observe for callback. I see that I might use owner and description to identify which slider was modified. But I don't think description supposed to be used for this purpose. After all, description should not need to be unique in the first place. I wonder if I am missing something here.
from ipywidgets import widgets
x=0.5
a=widgets.FloatSlider(min=0,max=1,description='a',value=x)
b=widgets.FloatSlider(min=0,max=1,description='b',value=1-x)
display(a,b)
def on_value_change(change):
if str(change['owner']).split("'")[1]=='a':
exec('b.value='+str(1-change['new']))
else:
exec('a.value='+str(1-change['new']))
a.observe(on_value_change,names='value')
b.observe(on_value_change,names='value')
Solution 1:[1]
Beginner with widgets, but I ran into the same question earlier and couldn't find a solution. I pieced together several sources and came up with something that seems to work.
Here's a model example of two sliders maintaining proportionality according to '100 = a + b', with two sliders representing a and b. :
caption = widgets.Label(value='If 100 = a + b :')
a, b = widgets.FloatSlider(description='a (=100-b)'),\
widgets.FloatSlider(description='b (= 100-a)')
def my_func1(a):
# b as function of a
return (100 - a)
def my_func2(b):
# a as function of b
return (100 - b)
l1 = widgets.dlink((a, 'value'), (b, 'value'),transform=my_func1)
l2 = widgets.dlink((b, 'value'), (a, 'value'),transform=my_func2)
display(caption, a, b)
To explain, as best as I understand... the key was to set up a directional link going each direction between the two sliders, and to provide a transform function for the math each direction across the sliders. i.e.,:
l1 = widgets.dlink((a, 'value'), (b, 'value'),transform=my_func1)
What that is saying is this: .dlink((a, 'value'), (b, 'value'),transform=my_func1)
is like "the value of a is a variable (input) used to determine the value of b (output)" and that "the function describing b, as a function of a, is my_func1".
With the links described, you just need to define the aforementioned functions.
The function pertaining to direct link l1 is:
def my_func1(a): # defining b as function of a
return (100 - a)
Likewise (but in reverse), l2
is the 'vice versa' to l1, and my_func2
the 'vice versa' to my_func1.
I found this to work better for learning purposes, compared to the fairly common approach of utilizing a listener (a.observe
or b.observe
) to log details (e.g. values) about the states of the sliders into a dictionary-type parameter (change
) which can be passed into the transform functions and indexing for variable assignments.
Good luck, hope that helps! More info at https://ipywidgets.readthedocs.io/en/latest/examples/Widget%20Events.html#Linking-Widgets
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | Mitchell Miller |