{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "import panel as pn\n",
    "import ipywidgets as ipw\n",
    "\n",
    "pn.extension('ipywidgets')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The ``IPyWidget`` pane renders any ipywidgets model both in the notebook and in a deployed server. This makes it possible to leverage this growing ecosystem directly from within Panel simply by wrapping the component in a Pane or Panel.\n",
    "\n",
    "In the notebook this is not necessary since Panel simply uses the regular notebook ipywidget renderer. Particularly in JupyterLab importing the ipywidgets extension in this way may interfere with the UI and render the JupyterLab UI unusable, so enable the extension with care.\n",
    "\n",
    "#### Parameters:\n",
    "\n",
    "For layout and styling related parameters see the [customization user guide](../../user_guide/Customization.ipynb).\n",
    "\n",
    "* **``object``** (object): The ipywidget object being displayed\n",
    "\n",
    "##### Display\n",
    "\n",
    "* **``default_layout``** (pn.layout.Panel, default=Row): Layout to wrap the plot and widgets in\n",
    "\n",
    "___"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The `panel` function will automatically convert any ``ipywidgets`` object into a displayable panel, while keeping all of its interactive features:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "date   = ipw.DatePicker(description='Date')\n",
    "slider = ipw.FloatSlider(description='Float')\n",
    "play   = ipw.Play()\n",
    "\n",
    "layout = ipw.HBox(children=[date, slider, play])\n",
    "\n",
    "pn.panel(layout)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Interactivity and callbacks"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Any ipywidget with a `value` parameter can also be used in a `pn.depends` decorated callback, e.g. here we declare a function that depends on the value of a `FloatSlider`:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "slider = ipw.IntSlider(description='Slider', min=-5, max=5)\n",
    "\n",
    "@pn.depends(slider)\n",
    "def cb(value):\n",
    "    return 'The slider value is ' + (\n",
    "        'negative' if value < 0 else 'nonnegative'\n",
    "    )\n",
    "\n",
    "pn.Row(slider, cb)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "If instead you want to write a callback yourself you can also use the `traitlets` `observe` method as you would usually. To read more about this see the [Widget Events](https://ipywidgets.readthedocs.io/en/stable/examples/Widget%20Events.html) section of the ipywidgets documentation."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "caption = ipw.Label(value='The slider value is nonnegative')\n",
    "slider = ipw.IntSlider(min=-5, max=5, value=1, description='Slider')\n",
    "\n",
    "def handle_slider_change(change):\n",
    "    caption.value = 'The slider value is ' + (\n",
    "        'negative' if change.new < 0 else 'nonnegative'\n",
    "    )\n",
    "\n",
    "slider.observe(handle_slider_change, names='value')\n",
    "\n",
    "pn.Row(slider, caption)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### External widget libraries"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The `IPyWidget` panel is also not restricted to simple widgets, ipywidget libraries such as [`ipyvolume`](https://ipyvolume.readthedocs.io/en/latest/index.html) and [`ipyleaflet`](https://ipyleaflet.readthedocs.io/en/latest/) are also supported."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import ipyvolume as ipv\n",
    "x, y, z, u, v = ipv.examples.klein_bottle(draw=False)\n",
    "fig = ipv.figure()\n",
    "m = ipv.plot_mesh(x, y, z, wireframe=False)\n",
    "ipv.squarelim()\n",
    "\n",
    "pn.panel(fig)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from ipyleaflet import Map, VideoOverlay\n",
    "\n",
    "m = Map(center=(25, -115), zoom=4)\n",
    "\n",
    "video = VideoOverlay(\n",
    "    url=\"https://www.mapbox.com/bites/00188/patricia_nasa.webm\",\n",
    "    bounds=((13, -130), (32, -100))\n",
    ")\n",
    "\n",
    "m.add_layer(video);\n",
    "\n",
    "pn.panel(m)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Limitations\n",
    "\n",
    "The ipywidgets support has some limitations because it is integrating two very distinct ecosystems. In particular it is not yet possible to set up JS-linking between a Panel and an ipywidget object or support for embedding. These limitations are not fundamental technical limitations and may be solved in future."
   ]
  }
 ],
 "metadata": {
  "language_info": {
   "name": "python",
   "pygments_lexer": "ipython3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
