{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import panel as pn\n",
    "pn.extension('plotly')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The ``HoloViews`` pane renders HoloViews plots with one of the plotting backends supported by HoloViews. It supports the regular HoloViews widgets for exploring the key dimensions of a ``HoloMap`` or ``DynamicMap``, but is more flexible than the native HoloViews widgets since it also allows customizing widget types and their position relative to the plot.\n",
    "\n",
    "#### Parameters:\n",
    "\n",
    "For details on other options for customizing the component see the [layout](../../how_to/layout/index.md) and [styling](../../how_to/styling/index.md) how-to guides.\n",
    "\n",
    "* **``backend``** (str): Any of the supported HoloViews backends ('bokeh', 'matplotlib', or 'plotly')\n",
    "* **``center``** (boolean, default=False): Whether to center the plot\n",
    "* **``linked_axes``** (boolean, default=True): Whether to link axes across plots in a panel layout\n",
    "* **``object``** (object): The HoloViews object being displayed\n",
    "* **``widget_location``** (str): Where to lay out the widget relative to the plot \n",
    "* **``widget_layout``** (ListPanel type): The object to lay the widgets out in, one of ``Row``, ``Column`` or ``WidgetBox``\n",
    "* **``widget_type``** (str): Whether to generate individual widgets for each dimension, or to use a global linear scrubber with dimensions concatenated.\n",
    "* **``widgets``** (dict): A mapping from dimension name to a widget class, instance, or dictionary of overrides to modify the default widgets.\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 ``HoloViews`` object into a displayable panel, while keeping all of its interactive features:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "import holoviews as hv\n",
    "\n",
    "box = hv.BoxWhisker((np.random.randint(0, 10, 100), np.random.randn(100)), 'Group').sort()\n",
    "\n",
    "hv_layout = pn.panel(box)\n",
    "hv_layout"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "By setting the pane's ``object`` the plot can be updated like all other pane objects:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "hv_layout.object = hv.Violin(box).opts(violin_color='Group', cmap='Category20')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Widgets\n",
    "\n",
    "HoloViews natively renders plots with widgets if a HoloMap or DynamicMap declares any key dimensions. Unlike Panel's ``interact`` functionality, this approach efficiently updates just the data inside a plot instead of replacing it entirely. Calling ``pn.panel`` on the DynamicMap will return a ``Row`` layout (configurable via the ``default_layout`` option), which is equivalent to calling ``pn.pane.HoloViews(dmap).layout``:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import pandas as pd\n",
    "import hvplot.pandas\n",
    "import holoviews.plotting.bokeh\n",
    "\n",
    "def sine(frequency=1.0, amplitude=1.0, function='sin'):\n",
    "    xs = np.arange(200)/200*20.0\n",
    "    ys = amplitude*getattr(np, function)(frequency*xs)\n",
    "    return pd.DataFrame(dict(y=ys), index=xs).hvplot()\n",
    "\n",
    "dmap = hv.DynamicMap(sine, kdims=['frequency', 'amplitude', 'function']).redim.range(\n",
    "    frequency=(0.1, 10), amplitude=(1, 10)).redim.values(function=['sin', 'cos', 'tan'])\n",
    "\n",
    "hv_panel = pn.panel(dmap)\n",
    "\n",
    "print(hv_panel)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We can see the widgets generated for each of the dimensions and arrange them any way we like, e.g. by unpacking them into a ``Row``:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "widgets = hv_panel[1]\n",
    "\n",
    "pn.Column(\n",
    "    pn.Row(*widgets),\n",
    "    hv_panel[0])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "However, more conveniently the HoloViews pane offers options to lay out the plot and widgets in a number of preconfigured arrangements using the ``center`` and ``widget_location`` parameters."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "pn.panel(dmap, center=True, widget_location='right_bottom')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The ``widget_location`` parameter accepts all of the following options:\n",
    "    \n",
    "    ['left', 'bottom', 'right', 'top', 'top_left', 'top_right', 'bottom_left',\n",
    "     'bottom_right', 'left_top', 'left_bottom', 'right_top', 'right_bottom']"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Customizing widgets\n",
    "\n",
    "As we saw above, the HoloViews pane will automatically try to generate appropriate widgets for the type of data, usually defaulting to ``DiscreteSlider`` and ``Select`` widgets. This behavior can be modified by providing a dictionary of ``widgets`` by dimension name.  The values of this dictionary can override the default widget in one of three ways:\n",
    "\n",
    "* Supplying a ``Widget`` instance\n",
    "* Supplying a compatible ``Widget`` type\n",
    "* Supplying a dictionary of ``Widget`` parameter overrides\n",
    "    \n",
    "``Widget`` instances will be used as they are supplied and are expected to provide values matching compatible with the values defined on HoloMap/DynamicMap. Similarly if a ``Widget`` type is supplied it should be discrete if the parameter space defines a discrete set of values.  If the defined parameter space is continuous, on the other hand, it may supply any valid value.\n",
    "\n",
    "In the example below the 'amplitude' dimension is overridden with an explicit ``Widget`` instance, the 'function' dimension is overridden with a RadioButtonGroup letting us toggle between the different functions, and lastly the 'value' parameter on the 'frequency' widget is overridden to change the initial value:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "hv_panel = pn.pane.HoloViews(dmap, widgets={\n",
    "    'amplitude': pn.widgets.LiteralInput(value=1., type=(float, int)),\n",
    "    'function': pn.widgets.RadioButtonGroup,\n",
    "    'frequency': {'value': 5}\n",
    "}).layout"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Switching backends\n",
    "\n",
    "The ``HoloViews`` pane will default to the Bokeh backend if no backend has been loaded, but you can override the backend as needed."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import holoviews.plotting.mpl\n",
    "import holoviews.plotting.plotly\n",
    "\n",
    "hv_pane = pn.pane.HoloViews(dmap, backend='matplotlib')\n",
    "hv_pane"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Please note that in a *server context* you will also have to set the matplotlib backend like below\n",
    "\n",
    "```python\n",
    "import matplotlib\n",
    "matplotlib.use('agg')\n",
    "```\n",
    "\n",
    "The ``backend``, like all other parameters, can be modified after the fact.  To demonstrate, we can set up a select widget to toggle between backends for the above plot:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "backend_select = pn.widgets.RadioButtonGroup(name='Backend Selector:', options=['bokeh', 'matplotlib', 'plotly'])\n",
    "backend_select.link(hv_pane[0], value='backend')\n",
    "backend_select"
   ]
  }
 ],
 "metadata": {
  "language_info": {
   "name": "python",
   "pygments_lexer": "ipython3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
