title: ipywidgets: How to make a double slider widget that accepts a callback for its observe method
body: <p>I am trying to create an object that behaves like an <code>ipywidget</code>, but contains two sliders instead of a single one. This object would have a <code>value</code> property that would return a <code>dict</code> of values (one entry for each slider).</p>
  <p>I would also like this widget to be embeddable inside other container widgets (<code>VBox</code>, <code>HBox</code>).</p>
  <p>I can create a custom class which contains two sliders, and looks like a widget by giving it the proper <code>_repr_mimebundle_</code>:</p>
  <pre class="lang-py prettyprint-override"><code>import ipywidgets as ipw
  
  class SliderWidget:
      def __init__(self):
          self._sliders = {name: ipw.IntSlider(description=name) for name in ('slider1', 'slider2')}
  
      def observe(self, callback, **kwargs):
          for sl in self._sliders.values():
              sl.observe(callback, **kwargs)
  
      @property
      def value(self):
          return {name: sl.value for name, sl in self._sliders.items()}
  
      @value.setter
      def value(self, val):
          for name, v in val.items():
              self._sliders[name].value = v
      
      def _repr_mimebundle_(self, include=None, exclude=None):
          return ipw.VBox(list(self._sliders.values()))._repr_mimebundle_(include=include, exclude=exclude)
  </code></pre>
  <p>which then looks as it should</p>
  <p><a href="https://i.stack.imgur.com/hNHeo.png" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/hNHeo.png" alt="enter image description here" /></a></p>
  <p>and also works well with the <code>observe</code> method</p>
  <pre class="lang-py prettyprint-override"><code>def func(change):
      print(change)
  
  sw.observe(func, names='value')
  </code></pre>
  <p>However, it cannot be embedded in other widgets, because it is not a Widget itself:</p>
  <pre class="lang-py prettyprint-override"><code>ipw.HBox([sw, sw])
  
  TraitError: The 'children' trait of a HBox instance contains an Instance of a TypedTuple which expected a Widget, not the SliderWidget at '0x7fbb10757b20'.
  </code></pre>
  <p>To fix this, I have tried to inherit from <code>VBox</code>, which works nicely</p>
  <pre class="lang-py prettyprint-override"><code>class SliderWidget(ipw.VBox):
      def __init__(self):
          self._sliders = {name: ipw.IntSlider(description=name) for name in ('slider1', 'slider2')}
          super().__init__(list(self._sliders.values()))
  
      # def observe(self, callback, **kwargs):
      #     for sl in self._sliders.values():
      #         sl.observe(callback, **kwargs)
  
      @property
      def value(self):
          return {name: sl.value for name, sl in self._sliders.items()}
  
      @value.setter
      def value(self, val):
          for name, v in val.items():
              self._sliders[name].value = v
  </code></pre>
  <p>and can be embedded inside other widgets
  <a href="https://i.stack.imgur.com/GzljQ.png" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/GzljQ.png" alt="enter image description here" /></a></p>
  <p>but unfortunately, I cannot seem to get the <code>observe</code> to work.
  If I uncomment the overriding of the <code>observe</code> method, I get this error upon creating the widget:</p>
  <pre><code>---------------------------------------------------------------------------
  TypeError                                 Traceback (most recent call last)
  Input In [68], in &lt;cell line: 1&gt;()
  ----&gt; 1 sw = SliderWidget()
        2 sw
  
  File ~/software/miniconda3/envs/mini/lib/python3.9/site-packages/traitlets/traitlets.py:1183, in HasDescriptors.__new__(*args, **kwargs)
     1181 else:
     1182     inst = new_meth(cls, *args, **kwargs)
  -&gt; 1183 inst.setup_instance(*args, **kwargs)
     1184 return inst
  
  File ~/software/miniconda3/envs/mini/lib/python3.9/site-packages/traitlets/traitlets.py:1224, in HasTraits.setup_instance(*args, **kwargs)
     1222 self._trait_validators = {}
     1223 self._cross_validation_lock = False
  -&gt; 1224 super(HasTraits, self).setup_instance(*args, **kwargs)
  
  File ~/software/miniconda3/envs/mini/lib/python3.9/site-packages/traitlets/traitlets.py:1206, in HasDescriptors.setup_instance(*args, **kwargs)
     1204 else:
     1205     if isinstance(value, BaseDescriptor):
  -&gt; 1206         value.instance_init(self)
  
  File ~/software/miniconda3/envs/mini/lib/python3.9/site-packages/traitlets/traitlets.py:1148, in ObserveHandler.instance_init(self, inst)
     1147 def instance_init(self, inst):
  -&gt; 1148     inst.observe(self, self.trait_names, type=self.type)
  
  TypeError: observe() takes 2 positional arguments but 3 were given
  </code></pre>