notebook.py
%23%20%2F%2F%2F%20script%0A%23%20requires-python%20%3D%20%22%3E%3D3.12%22%0A%23%20dependencies%20%3D%20%5B%0A%23%20%20%20%20%20%22altair%3D%3D5.5.0%22%2C%0A%23%20%20%20%20%20%22marimo%22%2C%0A%23%20%20%20%20%20%22matplotlib%3D%3D3.10.1%22%2C%0A%23%20%20%20%20%20%22mohtml%3D%3D0.1.7%22%2C%0A%23%20%20%20%20%20%22numpy%3D%3D2.2.5%22%2C%0A%23%20%20%20%20%20%22polars%3D%3D1.29.0%22%2C%0A%23%20%5D%0A%23%20%2F%2F%2F%0A%0Aimport%20marimo%0A%0A__generated_with%20%3D%20%220.19.6%22%0Aapp%20%3D%20marimo.App()%0A%0A%0A%40app.cell%0Adef%20_()%3A%0A%20%20%20%20import%20marimo%20as%20mo%0A%20%20%20%20import%20polars%20as%20pl%0A%20%20%20%20import%20numpy%20as%20np%0A%20%20%20%20return%20mo%2C%20np%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20%23%23%20Updating%20%60matplotlib%60%20charts%0A%0A%20%20%20%20The%20easiest%20way%20to%20update%20matplotlib%20charts%20is%20to%20first%20write%20a%20function%20that%20can%20generate%20a%20chart.%20The%20most%20common%20way%20to%20use%20matplotlib%20is%20to%20use%20syntax%20like%20%60plt.plot(...)%60%20followed%20by%20a%20%60plt.show(...)%60%20and%20the%20best%20way%20to%20capture%20all%20of%20these%20layers%20is%20to%20wrap%20them%20all%20ina%20single%20function.%20Once%20you%20have%20such%20a%20function%2C%20you%20can%20use%20the%20%60%40refresh_matplotlib%60%20decorator%20to%20turn%20this%20function%20into%20something%20that%20we%20can%20use%20in%20a%20refreshable-chart.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(np)%3A%0A%20%20%20%20import%20matplotlib.pylab%20as%20plt%0A%20%20%20%20from%20wigglystuff.utils%20import%20refresh_matplotlib%0A%0A%20%20%20%20%40refresh_matplotlib%0A%20%20%20%20def%20cumsum_linechart(data)%3A%0A%20%20%20%20%20%20%20%20y%20%3D%20np.cumsum(data)%0A%20%20%20%20%20%20%20%20plt.plot(np.arange(len(y))%2C%20y)%0A%20%20%20%20return%20(cumsum_linechart%2C)%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20The%20decorator%20takes%20the%20matplotlib%20image%20and%20turns%20it%20into%20a%20base64%20encoded%20string%20that%20can%20be%20plotted%20by%20%60%3Cimg%3E%60%20tags%20in%20html.%20You%20can%20see%20this%20for%20yourself%20in%20the%20example%20below.%20The%20%60img(src%3D...)%60%20function%20call%20in%20%60mohtml%60%20is%20effectively%20a%20bit%20of%20syntactic%20sugar%20around%20%60%3Cimg%20src%3D%22...%22%3E%60.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(cumsum_linechart)%3A%0A%20%20%20%20from%20mohtml%20import%20img%20%0A%0A%20%20%20%20img(src%3Dcumsum_linechart(%5B1%2C%202%2C%203%2C%202%5D))%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20Having%20a%20static%20image%20is%20great%2C%20but%20we%20want%20dynamic%20images!%20That's%20where%20our%20%60ImageRefreshWidget%60%20comes%20in.%20It%20allows%20you%20to%20trigger%20a%20streaming%20update%20to%20an%20image%20by%20running%20code%20from%20another%20cell.%20Try%20it%20out%20below!%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(cumsum_linechart)%3A%0A%20%20%20%20from%20wigglystuff%20import%20ImageRefreshWidget%0A%0A%20%20%20%20widget%20%3D%20ImageRefreshWidget(%0A%20%20%20%20%20%20%20%20src%3Dcumsum_linechart(%5B1%2C2%2C3%2C4%5D)%0A%20%20%20%20)%0A%20%20%20%20widget%0A%20%20%20%20return%20(widget%2C)%0A%0A%0A%40app.cell%0Adef%20_(cumsum_linechart%2C%20random%2C%20time%2C%20widget)%3A%0A%20%20%20%20data%20%3D%20%5Brandom.random()%20-%200.5%5D%0A%0A%20%20%20%20for%20i%20in%20range(20)%3A%0A%20%20%20%20%20%20%20%20data%20%2B%3D%20%5Brandom.random()%20-%200.5%5D%0A%20%20%20%20%20%20%20%20%23%20This%20one%20line%20over%20here%20causes%20the%20update!%0A%20%20%20%20%20%20%20%20widget.src%20%3D%20cumsum_linechart(data)%0A%20%20%20%20%20%20%20%20time.sleep(0.2)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20When%20you%20re-run%20the%20cell%20below%20you%20should%20see%20that%20the%20widget%20updates.%20This%20works%20because%20the%20widget%20knows%20how%20to%20respond%20to%20a%20change%20to%20the%20%60widget.src%60%20property.%20You%20only%20need%20to%20make%20sure%20that%20you%20pass%20along%20a%20base64%20string%20that%20html%20images%20can%20handle%2C%20which%20is%20covered%20by%20the%20decorator%20that%20we%20applied%20earlier.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20%23%23%20Updating%20%60altair%60%20charts%0A%0A%20%20%20%20This%20library%20can%20also%20deal%20with%20altair%20charts.%20This%20works%20by%20turning%20the%20chart%20into%20an%20SVG.%20This%20is%20a%20static%20representation%20that%20does%20not%20require%20any%20javascript%20to%20run%2C%20which%20means%20that%20we%20can%20apply%20a%20similar%20pattern%20as%20before!%0A%0A%20%20%20%20%3E%20Due%20to%20a%20required%20dependency%20to%20convert%20the%20altair%20chart%20to%20SVG%20we%20cannot%20run%20the%20altair%20demo%20in%20WASM.%20This%20code%20will%20run%20just%20fine%20locally%20on%20your%20machine%20but%20currently%20breaks%20on%20the%20Github%20pages%20deployment.%0A%0A%20%20%20%20%60%60%60python%0A%20%20%20%20import%20altair%20as%20alt%0A%20%20%20%20from%20wigglystuff%20import%20HTMLRefreshWidget%0A%20%20%20%20from%20wigglystuff.utils%20import%20refresh_altair%2C%20altair2svg%0A%0A%20%20%20%20%40refresh_altair%0A%20%20%20%20def%20altair_cumsum_chart(data)%3A%0A%20%20%20%20%20%20%20%20df%20%3D%20pl.DataFrame(%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%22x%22%3A%20range(len(data))%2C%20%22y%22%3A%20np.array(data).cumsum()%0A%20%20%20%20%20%20%20%20%7D)%0A%20%20%20%20%20%20%20%20return%20alt.Chart(df).mark_line().encode(x%3D%22x%22%2C%20y%3D%22y%22)%0A%0A%20%20%20%20svg_widget%20%3D%20HTMLRefreshWidget(html%3Daltair_cumsum_chart(%5B1%2C%202%5D))%0A%0A%20%20%20%20more_data%20%3D%20%5Brandom.random()%20-%200.5%20for%20_%20in%20range(10)%5D%0A%0A%20%20%20%20for%20_i%20in%20range(10)%3A%0A%20%20%20%20%20%20%20%20more_data%20%2B%3D%20%5Brandom.random()%20-%200.5%5D%0A%20%20%20%20%20%20%20%20svg_widget.html%20%3D%20altair_cumsum_chart(more_data)%0A%20%20%20%20%20%20%20%20time.sleep(0.1)%0A%0A%20%20%20%20for%20_i%20in%20range(10)%3A%0A%20%20%20%20%20%20%20%20more_data%20%2B%3D%20%5Brandom.random()%20-%200.5%5D%0A%20%20%20%20%20%20%20%20svg_widget.html%20%3D%20altair_cumsum_chart(more_data)%0A%20%20%20%20%20%20%20%20time.sleep(0.1)%0A%20%20%20%20%60%60%60%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20Unlike%20matplotlib%20charts%20though%2C%20altair%20is%20actually%20designed%20to%20give%20you%20objects%20back.%20That%20means%20that%20you%20don't%20need%20to%20use%20a%20decorated%20function%20for%20the%20update%2C%20you%20can%20also%20just%20convert%20the%20altair%20chart%20to%20SVG%20directly.%20This%20library%20supports%20utilities%20for%20both%20patterns.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20%23%23%20Oh%20...%20one%20more%20thing%20about%20that%20%60HTMLRefreshWidget%60%0A%0A%20%20%20%20We%20are%20injecting%20html%20now%20into%20that%20widget%20to%20allow%20us%20to%20draw%20altair%20charts.%20But%20why%20stop%20there%3F%20We%20can%20put%20in%20any%20HTML%20that%20we%20like!%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(mo)%3A%0A%20%20%20%20from%20wigglystuff%20import%20HTMLRefreshWidget%0A%0A%20%20%20%20html_widget%20%3D%20mo.ui.anywidget(HTMLRefreshWidget())%0A%20%20%20%20html_widget%0A%20%20%20%20return%20(html_widget%2C)%0A%0A%0A%40app.cell%0Adef%20_(html_widget%2C%20time)%3A%0A%20%20%20%20for%20_i%20in%20range(10)%3A%0A%20%20%20%20%20%20%20%20html_widget.html%20%3D%20f%22%3Cp%3ECounting%20%7B_i%7D%3C%2Fp%3E%22%0A%20%20%20%20%20%20%20%20time.sleep(0.1)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20%23%23%20Progress%20bars%0A%0A%20%20%20%20We%20also%20provide%20progress%20bars%20that%20can%20update%20inplace%20without%20needing%20any%203rd%20party%20ipywidget%20tools.%20These%20should%20still%20work%20across%20all%20notebooks.%20These%20progress%20bars%20can%20also%20be%20updated%20from%20another%20cell%20in%20marimo%2C%20which%20the%20base%20progress%20bar%20does%20not%20allow.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_()%3A%0A%20%20%20%20from%20wigglystuff%20import%20ProgressBar%0A%0A%20%20%20%20progress%20%3D%20ProgressBar(value%3D0%2C%20max_value%3D100)%0A%20%20%20%20progress%0A%20%20%20%20return%20(progress%2C)%0A%0A%0A%40app.cell%0Adef%20_(progress%2C%20random%2C%20time)%3A%0A%20%20%20%20def%20slow_task()%3A%0A%20%20%20%20%20%20%20%20%22%22%22Simulated%20task%20that%20takes%20time%22%22%22%0A%20%20%20%20%20%20%20%20time.sleep(random.random()%20%2F%2010)%0A%0A%20%20%20%20progress.value%20%3D%200%20%0A%20%20%20%20for%20_%20in%20range(100)%3A%0A%20%20%20%20%20%20%20%20slow_task()%0A%20%20%20%20%20%20%20%20progress.value%20%2B%3D%201%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_()%3A%0A%20%20%20%20import%20random%20%0A%20%20%20%20import%20time%20%0A%20%20%20%20return%20random%2C%20time%0A%0A%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20app.run()%0A