hanno
hanno

Reputation: 6513

Pulling ipywidget value manually

I have this code ipython code:

import ipywidgets as widgets
from IPython.display import display
import time

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

def on_change(change):
    print("changed to %s" % change['new'])

w.observe(on_change)

display(w)

It works as expected. When the value of the widget changes, the on_change function gets triggered. However, I want to run a long computation and periodically check for updates to the widget. For example:

for i in range(100):
    time.sleep(1)
    # pull for changes to w here.
    # if w.has_changed:
    #     print(w.value)

How can I achieve this?

Upvotes: 3

Views: 835

Answers (4)

Andy
Andy

Reputation: 3215

A slight improvement to the ipython.kernel.do_one_iteration call used

# Max iteration limit, in case I don't know what I'm doing here...
for _ in range(100):
  ipython.kernel.do_one_iteration()
  if ipython.kernel.msg_queue.empty():
    break

In my case, I had a number of UI elements, that could be clicked multiple times between do_one_iteration calls, which will process them one at a time, and with a 1 second time delay, that could get annoying. This will process at most 100 at a time. Tested it by mashing a button multiple times, and now they all get processes as soon as the sleep(1) ends.

Upvotes: 0

Zak
Zak

Reputation: 981

To elaborate on the OP's self answer, this does work. It forces the widgets to sync with the kernel at an arbitrary point in the loop. This can be done right before the accessing the widget.value.

So the full solution would be:

import IPython
ipython = IPython.get_ipython()

last_val = 0
for i in range(100):
    time.sleep(1)
    ipython.kernel.do_one_iteration()
    new_val = w.value
    if new_val != old_val:
        print(new_val)
        old_val = new_val

Upvotes: 0

hanno
hanno

Reputation: 6513

For reference, I seem to be able to do the desired polling with

import IPython
ipython = IPython.get_ipython()
ipython.kernel.do_one_iteration()

(I'd still love to have some feedback on whether this works by accident or design.)

Upvotes: 2

ngoldbaum
ngoldbaum

Reputation: 5590

I think you need to use threads and hook into the ZMQ event loop. This gist illustrates an example:

https://gist.github.com/maartenbreddels/3378e8257bf0ee18cfcbdacce6e6a77e

Also see https://github.com/jupyter-widgets/ipywidgets/issues/642.

Upvotes: 0

Related Questions