1

I am trying to learn how ipywidget dropdown menu works with an observe method and came across to this really useful SO question: ipywidgets dropdown widgets: what is the onchange event?

Good explanations can be found there.

Tweaking a bit some code of the answers I created this little script:

w = wd.Dropdown(
    options=['Addition', 'Multiplication', 'Subtraction'],
    value='Addition',
    description='Task:',
)

def on_change(change):
    print('method is called when printing this')
    if change['type'] == 'change' and change['name'] == 'value':
        print("changed to %s" % change.new)
    else:
        print('chage type is not change it is actually:', change['type'])
        print('chage name is not value it is actually:', change['name'])

w.observe(on_change)

display(w)

The weird thing is that when changing the value of the dropdown menu ONE TIME this is what is printed out:

method is called when printing this
chage type is not change it is actually: change
chage name is not value it is actually: _property_lock
method is called when printing this
chage type is not change it is actually: change
chage name is not value it is actually: label
method is called when printing this
changed to Multiplication
method is called when printing this
chage type is not change it is actually: change
chage name is not value it is actually: index
method is called when printing this
chage type is not change it is actually: change
chage name is not value it is actually: _property_lock

So the observe method is called 4 times for one single change of the dropdown of the menu.

Why is that?

secondly this does not happened when the observe is written like this:

w.observe(on_change, names='value')

Then the output is just:

method is called when printing this
changed to Multiplication

So in the second case the method is called one once.

Can someone explain what is going on here?

JFerro
  • 3,203
  • 7
  • 35
  • 88

1 Answers1

3

In

w.observe(on_change)

you're telling the widget to call the on_change function everytime any of its attribute (actually Trait attribute I guess), is changed: this includes the value (which is only what you want most of the time) but also all of its more "internal attributes" (like _property_lock, which you don't need most of the time). This behaviour is documented looking at the w.observe doc :

Signature: w.observe(handler, names=traitlets.All, type='change')
Docstring:
Setup a handler to be called when a trait changes.

This is used to setup dynamic notifications of trait changes.

Parameters
----------
handler : callable
    A callable that is called when a trait changes. Its
    signature should be ``handler(change)``, where ``change`` is a
    dictionary. The change dictionary at least holds a 'type' key.
    * ``type``: the type of notification.
    Other keys may be passed depending on the value of 'type'. In the
    case where type is 'change', we also have the following keys:
    * ``owner`` : the HasTraits instance
    * ``old`` : the old value of the modified trait attribute
    * ``new`` : the new value of the modified trait attribute
    * ``name`` : the name of the modified trait attribute.
names : list, str, All
    If names is All, the handler will apply to all traits.  If a list
    of str, handler will apply to all names in the list.  If a
    str, the handler will apply just to that name.
type : str, All (default: 'change')
    The type of notification to filter by. If equal to All, then all
    notifications are passed to the observe handler.

the names defaults to traitlets.All and in this case : If names is All, the handler will apply to all traits.

Hence the importance of the names=Value when defining you callback.

mocquin
  • 402
  • 3
  • 11