Some consideration:
- rather than threads, consider using the event loop
- if you absolutely need threads, consider still using
asyncio
, and a deque
- if you absolutely need threads, consider still using
- maybe have a look at using
ipympl
- doing full display updates are pretty expensive
Here’s a basic pattern: it delegates the asyncio stuff to an interval-based loop. Ideally, the do_work
would also be async
, but matplotlib isn’t.
import ipywidgets, asyncio, time
output = ipywidgets.Textarea()
running = ipywidgets.ToggleButton(description="start", icon="play")
interval = ipywidgets.FloatSlider(description="loop interval", value=1, min=0.001, max=10) # zero can hang if you don't await
ui = ipywidgets.VBox([ipywidgets.HBox([running, interval]), output])
tasks = dict()
def do_work(t):
output.value = f"{t}: {time.time()}"
async def do_loop():
t = 0
while running.value:
do_work(t)
t += 1
await asyncio.sleep(interval.value)
def on_running_changed(*change):
task = tasks.pop("do_loop", None)
output.value = f"maybe stopping {task}..."
if task:
output.value = f"stopping {task}..."
task.cancel()
if running.value:
output.value = "starting..."
tasks["do_loop"] = asyncio.get_event_loop().create_task(do_loop())
running.observe(on_running_changed, "value")
ui