ipywidgets 事件去抖动

ipywidgets 事件去抖动#

# 为了保证 `JupyterLite` 可用,需要 notebook 开头添加:
%pip install -q ipywidgets
Note: you may need to restart the kernel to use updated packages.

当特质变化触发执行繁重计算的回调函数时,你可能不希望像值更新那样频繁地进行计算。例如,如果特质由一个滑块驱动,并且其 continuous_update 设置为 True,用户将触发一连串快速连续的计算。

去抖动通过延迟回调执行直到值在一定时间内没有改变来解决这个问题,之后使用最新值调用回调函数。效果是只有当特质暂停改变一定时间后才会调用回调函数。

去抖动可以使用异步循环或线程来实现。下面展示了一种更适合 ipywidgets 的异步解决方案。如果你希望改用线程来进行去抖动,可以将 Timer 类替换为 from threading import Timer

import asyncio

class Timer:
    def __init__(self, timeout, callback):
        self._timeout = timeout
        self._callback = callback

    async def _job(self):
        await asyncio.sleep(self._timeout)
        self._callback()

    def start(self):
        self._task = asyncio.ensure_future(self._job())

    def cancel(self):
        self._task.cancel()

def debounce(wait):
    """ Decorator that will postpone a function's
        execution until after `wait` seconds
        have elapsed since the last time it was invoked. """
    def decorator(fn):
        timer = None
        def debounced(*args, **kwargs):
            nonlocal timer
            def call_it():
                fn(*args, **kwargs)
            if timer is not None:
                timer.cancel()
            timer = Timer(wait, call_it)
            timer.start()
        return debounced
    return decorator

以下是我们如何使用 debounce 函数作为装饰器。尝试更改滑块的值。文本框将在滑块暂停大约 0.2 秒后才会更新。

import ipywidgets as widgets
slider = widgets.IntSlider()
text = widgets.IntText()

@debounce(0.2)
def value_changed(change):
    text.value = change.new
slider.observe(value_changed, 'value')

widgets.VBox([slider, text])