Flet で matplotlib をアニメーション表示する

Flutter ではなく Flet を使う場合のメリットとして考えられるのが numpy や matplotlib の使用だったので, matplotlib でアニメーションを表示してみる方法を調べた

検証環境

Flet について

目標

基本構成

コード全体

import flet as ft
import matplotlib.pyplot as plt
from flet.matplotlib_chart import MatplotlibChart
import numpy as np
import asyncio

animation_task = None

async def main(page: ft.Page):
    def route_change(e):
        page.views.clear()
        
        # routing
        if page.route == "/":
            page.views.append(main_page_view(page))
        elif page.route == "/plot":
            page_view = plot_page()
            page.views.append(page_view)
            
        # update
        page.update()
    
    page.on_route_change = route_change
    page.go("/") # home page
    
def main_page_view(page: ft.Page):
    return ft.View(
        "/",
        controls=[
            ft.SafeArea(ft.Text("Hello, FletApp!")),
            ft.ElevatedButton("to plot", on_click=lambda _: page.go("/plot"))
        ],
        appbar=ft.AppBar(
            title=ft.Text(
                value="Flet App", size=32,
                text_align="center", color=ft.colors.BLACK
            ),
            bgcolor=ft.colors.GREEN_200
        )
    )

def plot_page():
    fig = plt.figure()
    ax = fig.add_subplot()
    x = np.arange(100)
    y = np.sin(x * (np.pi / 10))
    plot_data = ax.plot(x, y)
    chart = MatplotlibChart(fig, expand=True)
    
    async def start_animation(e):
        global animation_task
        animation_task = asyncio.create_task(animation(fig, plot_data, chart))
        
    def end_animation(e):
        global animation_task
        if animation_task != None:
            animation_task.cancel()

    return ft.View( 
        "/plot",
        controls=[
            chart,
            ft.ElevatedButton("animation!", on_click=start_animation),
            ft.ElevatedButton("end animation", on_click=end_animation)
            ],
        appbar=ft.AppBar(
            title=ft.Text(
                value="Simulator", size=32,
                text_align="center", color=ft.colors.BLACK
            ),
            bgcolor=ft.colors.GREEN_200
        )
        )
    
async def animation(fig, plot_data, chart):
    t = 0
    while True:
        x = np.arange(100)
        y = np.sin((x + t) * (np.pi / 10))
        plot_data[0].set_ydata(y)
        chart.figure = fig
        chart.update()
        
        t += 0.1
        await asyncio.sleep(0.01)

ft.app(main)
        

matplotlib の表示

fig = plt.figure()
ax = fig.add_subplot()
x = np.arange(100)
y = np.sin(x * (np.pi / 10))
plot_data = ax.plot(x, y)
chart = MatplotlibChart(fig, expand=True)
        

非同期更新処理の記述

async def animation(fig, plot_data, chart):
    t = 0
    while True:
        x = np.arange(100)
        y = np.sin((x + t) * (np.pi / 10))
        plot_data[0].set_ydata(y)
        chart.figure = fig
        chart.update()
        
        t += 0.1
        await asyncio.sleep(0.01)
        

アニメーションの開始・終了処理の作成

async def start_animation(e):
    global animation_task
    animation_task = asyncio.create_task(animation(fig, plot_data, chart))
    
def end_animation(e):
    global animation_task
    if animation_task != None:
        animation_task.cancel()
        

完成品

flet animation