Python Event Loops
This guide demonstrates how to create Python-controlled animations using Colight plots, the .reset method, and interactive sliders. We'll cover:
1. Setting up a basic animated plot
2. Creating interactive animations with ipywidgets
We must use the "widget" rendering modes for bidirectional python/javascript communication:
import colight.plot as Plot
Plot.configure({"display_as": "widget"})
First, a simple sine wave plot:
import numpy as np
x = np.linspace(0, 10, 100)
basic_plot = (
Plot.line(list(zip(x, np.sin(x))))
+ Plot.domain([0, 10], [-1, 1])
+ Plot.height(200)
)
basic_plot
Now, let's animate it:
import asyncio
import time
async def animate(duration=5):
start_time = time.time()
while time.time() - start_time < duration:
t = time.time() - start_time
y = np.sin(x + t)
basic_plot.reset(
Plot.line(list(zip(x, y)))
+ Plot.domain([0, 10], [-1, 1])
+ Plot.height(200)
)
await asyncio.sleep(1 / 30) # 30 FPS
future = asyncio.ensure_future(animate())
We use the reset method of a plot to update its content in-place, inside an async function containing a while loop, using sleep to control the frame rate. To avoid interference with Jupyter comms, we use ensure_future to run the function in a new thread.
Let's make it interactive, using ipywidgets sliders to control frequency and amplitude:
import ipywidgets as widgets
interactive_plot = (
Plot.line(list(zip(x, np.sin(x))))
+ Plot.domain([0, 10], [-2, 2])
+ Plot.height(200)
)
frequency_slider = widgets.FloatSlider(
value=1.0, min=0.1, max=5.0, step=0.1, description="Frequency:"
)
amplitude_slider = widgets.FloatSlider(
value=1.0, min=0.1, max=2.0, step=0.1, description="Amplitude:"
)
Now, in our animation loop we use the slider values to compute the y value:
from IPython.display import display
async def interactive_animate(duration=10):
start_time = time.time()
while time.time() - start_time < duration:
t = time.time() - start_time
y = amplitude_slider.value * np.sin(frequency_slider.value * (x + t))
interactive_plot.reset(
Plot.line(list(zip(x, y)))
+ Plot.domain([0, 10], [-2, 2])
+ Plot.height(200)
)
await asyncio.sleep(1 / 30)
display(interactive_plot)
display(frequency_slider, amplitude_slider)
future = asyncio.ensure_future(interactive_animate())