{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Saving Animations\n", "\n", "Since the controls object knows how to update figures as the sliders change their values it is also able to save an animation (e.g. `.gif` or `.mp4` by updating the slider values for you. Under the hood this makes use of [FuncAnimation](https://matplotlib.org/api/_as_gen/matplotlib.animation.FuncAnimation.html?highlight=funcanimation#matplotlib.animation.FuncAnimation) and you can pass any relevant kwargs in via `func_anim_kwargs`. Other `kwargs` will passed to `animation.save`.\n", "\n", "Saving animations will work with either ipywidgets Sliders or with matplotlib Sliders. However, it will not work with other widgets. (This is an potential area of improvement, PRs welcome)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%matplotlib ipympl\n", "import matplotlib.pyplot as plt\n", "import numpy as np\n", "\n", "import mpl_interactions.ipyplot as iplt" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Basic Usage" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "x = np.linspace(0, 2 * np.pi, 200)\n", "\n", "\n", "def f(x, amp, freq):\n", " return amp * np.sin(x * freq)\n", "\n", "\n", "# Create the plot as normal\n", "fig, ax = plt.subplots()\n", "controls = iplt.plot(x, f, freq=(0.05, 10, 250), amp=(1,10))\n", "_ = iplt.title(\"the Frequency is: {freq:.2f}\", controls=controls[\"freq\"])" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# save as a gif\n", "anim = controls.save_animation(\"freq-plot-1.gif\", fig, \"freq\", interval=35)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Which Generates this GIF\n", "\n", "![gif of sin animated over frequency](freq-plot-1.gif)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Embeding the animation in a noteook.\n", "\n", "To embed the animation you can do:\n", "\n", "1. Link to it in markdown cell with `![alt-text](path/to/image)`\n", "2. Drag the file into a markdown cell\n", "3. Embed `anim.to_html5_video()` using IPython.display.Video:\n", "```python\n", "from IPython.display import Video\n", "Video(anim.to_html5_video(), embed=True)\n", "```\n", "\n", "4. Use IPython to display the saved gif\n", "\n", "You can also read more in this excellent blog post: http://louistiao.me/posts/notebooks/embedding-matplotlib-animations-in-jupyter-as-interactive-javascript-widgets/" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# NBVAL_IGNORE_OUTPUT\n", "from IPython.display import Image\n", "\n", "Image(\"freq-plot-1.gif\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Matplotlib Sliders with `valstep=None`\n", "\n", "Matplotlib sliders have an optional attribute `valstep` that allows for discrete slider steps. `mpl-interactions` uses this for all sliders that it creates, however if you passed a custom made slider in as a kwarg you may not have used `valstep` if this is the case then the `save_animation` function cannot infer how many frames it should render, so you can specify this with the `N_frames` arguments." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "from matplotlib.widgets import Slider\n", "\n", "import mpl_interactions.ipyplot as iplt\n", "\n", "fig, ax = plt.subplots()\n", "plt.subplots_adjust(bottom=0.25)\n", "x = np.linspace(0, 2 * np.pi, 200)\n", "\n", "\n", "def f(x, freq):\n", " return np.sin(x * freq)\n", "\n", "\n", "axfreq = plt.axes([0.25, 0.1, 0.65, 0.03])\n", "slider = Slider(axfreq, label=\"freq\", valmin=0.05, valmax=10) # note the lack of valstep\n", "controls2 = iplt.plot(x, f, freq=slider, ax=ax)\n", "_ = iplt.title(\"the Frequency is: {freq:.2f}\", controls=controls2[\"freq\"])" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# save as a gif\n", "anim2 = controls2.save_animation(\"freq-plot-2.gif\", fig, \"freq\", interval=35, N_frames=100)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Gives this GIF:\n", "\n", "![freq-plot-2.gif](freq-plot-2.gif)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.9.0" } }, "nbformat": 4, "nbformat_minor": 4 }