3

I'm trying to figure out how to link the value of a counter controlled by button widgets to the value of a slider widget.

The goal here is use ipython widgets to create a simple "vcr-like" interface with three widgets: an IntSlider and two Buttons that increment a counter and decrement a counter. This is what I've got:

import ipywidgets as widgets
from functools import partial
from IPython.display import display
import traitlets

class Counter:
   def __init__(self, initial=0):
      self.value = initial

   def increment(self, amount=1):
      self.value += amount
      return self.value

def button_plus(counter, w):
    counter.increment(+1)  

def button_minus(counter, w):
    counter.increment(-1) 

counter = Counter()
# 1 step forward button
wplus = widgets.Button(description='>')
wplus.on_click(partial(button_plus, counter))
# 1 step backward button
wminus = widgets.Button(description='<')
wminus.on_click(partial(button_minus, counter))
# integer slider
wpick = widgets.IntSlider(value=0,min=0,max=10,step=1,description="time step")

display(wminus, wpick, wplus)

print(counter.value)
print(wpick.value)

and here's a screen grab where I've moved the IntSlider to 1 and clicked twice on the increment button: enter image description here

I'd obviously like there to be a single integer value being controlled by and be in sync with all 3 widgets.

I read about widget linking but I don't see how to do this since my button widgets don't have a value -- the counter object has the value I want to link.

This doesn't work:

l = traitlets.link((counter, 'value'), (wpick, 'value'))

because counter is not HasTraits.

How can I get counter.value to be linked to wpick.value so that clicking on one of the buttons will adjust the int on the slider?

1 Answer 1

4

Following this guide, you need the Counter class to inheret from the DOMWidget class like this:

from traitlets import CInt, link
class Counter(widgets.DOMWidget):
    value = CInt(0, sync=True)

You can then define your counter widget and button callback methods:

counter = Counter()
def button_plus(name):
    counter.value += 1 if counter.value < 10 else 0
def button_minus(name):
    counter.value -= 1 if counter.value > 0 else 0

link the slider and counter widgets:

link((wpick, 'value'), (counter, 'value'))

and register the events on the buttons:

wplus.on_click(button_plus)
wminus.on_click(button_minus)

Clicking the buttons will now in/decrease the value of the counter.

Sign up to request clarification or add additional context in comments.

4 Comments

what does name refer to in your answer? Do you also have different definitions of wplus and wminus?
@RichSignell - name is the name of the widget calling the callback method. Its not used but atleast one input in the method definition is needed as the on_click sends name. I used the same definitions for wplus and wminus. Why? is it not working for you?
you are right. It works fine once I got rid of my coding errors: gist.github.com/rsignell-usgs/0067e0db0cfd8c3ed4bc
@RichSignell - Just a tip: In your gist you haven't defined the class method increment but sometimes it is still defined in the namespace from a previous run. To avoid this i always start my ipynb's with %reset -f%.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.