手动拉取 ipywidget 值

Pulling ipywidget value manually

我有这个代码ipython代码:

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)

它按预期工作。当小部件的值更改时,将触发 on_change 函数。但是,我想 运行 进行长时间的计算并定期检查小部件的更新。例如:

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

我怎样才能做到这一点?

我认为您需要使用线程并挂钩到 ZMQ 事件循环中。这个要点说明了一个例子:

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

另见 https://github.com/jupyter-widgets/ipywidgets/issues/642

作为参考,我似乎可以使用

进行所需的轮询
import IPython
ipython = IPython.get_ipython()
ipython.kernel.do_one_iteration()

(我仍然希望得到一些关于这是否是偶然或设计的反馈。)

为了详细说明 OP 的自我回答,这确实有效。它强制小部件在循环中的任意点与内核同步。这可以在访问 widget.value.

之前完成

所以完整的解决方案是:

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

对使用的 ipython.kernel.do_one_iteration 调用稍作改进

# 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

在我的例子中,我有许多 UI 元素,可以在 do_one_iteration 调用之间多次点击它们,一次处理一个,并用 1 秒延迟,这可能会很烦人。这一次最多处理 100 个。通过多次按下一个按钮来测试它,现在他们都在 sleep(1) 结束后立即获得进程。