{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import panel as pn\n",
    "pn.extension('plotly')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The ``Plotly`` pane renders Plotly plots inside a panel. It optimizes the plot rendering by using binary serialization for any array data found on the Plotly object, providing efficient updates. Note that to use the Plotly pane in a Jupyter notebook, the Panel extension has to be loaded with 'plotly' as an argument to ensure that Plotly.js is initialized.\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 Plotly figure being displayed\n",
    "* **``click_data``** (dict): Click callback data\n",
    "* **``clickannotation_data``** (dict): Clickannotation callback data\n",
    "* **``config``** (dict): Config data\n",
    "* **``hover_data``** (dict): Hover callback data\n",
    "* **``relayout_data``** (dict): Relayout callback data\n",
    "* **``restyle_data``** (dict): Restyle callback data\n",
    "* **``selected_data``** (dict): Selected callback data\n",
    "* **``viewport``** (dict): Current viewport state\n",
    "* **``viewport_update_policy``** (str, default = 'mouseup'): Policy by which the viewport parameter is updated during user interactions\n",
    "    * ``mouseup``: updates are synchronized when mouse button is released after panning\n",
    "    * ``continuoius``: updates are synchronized continually while panning\n",
    "    * ``throttle``: updates are synchronized while panning, at intervals determined by the viewport_update_throttle parameter\n",
    "* **``viewport_update_throttle``** (int, default = 200, bounds = (0, None)): Time interval in milliseconds at which viewport updates are synchronized when viewport_update_policy is \"throttle\".\n",
    "\n",
    "___"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "As with most other types ``Panel`` will automatically convert a Plotly figure to a ``Plotly`` pane if it is passed to the ``pn.panel`` function or a panel layout, but a ``Plotly`` pane can be constructed directly using the ``pn.pane.Plotly`` constructor:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "import plotly.graph_objs as go\n",
    "\n",
    "xx = np.linspace(-3.5, 3.5, 100)\n",
    "yy = np.linspace(-3.5, 3.5, 100)\n",
    "x, y = np.meshgrid(xx, yy)\n",
    "z = np.exp(-(x-1)**2-y**2)-(x**3+y**4-x/5)*np.exp(-(x**2+y**2))\n",
    "\n",
    "surface = go.Surface(z=z)\n",
    "layout = go.Layout(\n",
    "    title='Plotly 3D Plot',\n",
    "    autosize=False,\n",
    "    width=500,\n",
    "    height=500,\n",
    "    margin=dict(t=50, b=50, r=50, l=50)\n",
    ")\n",
    "fig = dict(data=[surface], layout=layout)\n",
    "\n",
    "plotly_pane = pn.pane.Plotly(fig)\n",
    "plotly_pane"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Once created the plot can be updated by modifying the Plotly traces and then triggering an update by setting or triggering an event on the pane ``object``. Note that this only works if the ``Figure`` is defined as a dictionary, since Plotly will make copies of the traces, which means that modifying them in place has no effect. Modifying an array will send just the array using a binary protocol, leading to fast and efficient updates."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "surface.z = np.sin(z+1)\n",
    "plotly_pane.object = fig"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Similarly, modifying the plot ``layout`` will only modify the layout, leaving the traces unaffected."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "fig['layout']['width'] = 800\n",
    "\n",
    "plotly_pane.object = fig"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The Plotly pane supports layouts and subplots of arbitrary complexity, allowing even deeply nested Plotly figures to be displayed:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from plotly import subplots\n",
    "\n",
    "heatmap = go.Heatmap(\n",
    "    z=[[1, 20, 30],\n",
    "       [20, 1, 60],\n",
    "       [30, 60, 1]],\n",
    "    showscale=False)\n",
    "\n",
    "y0 = np.random.randn(50)\n",
    "y1 = np.random.randn(50)+1\n",
    "\n",
    "box_1 = go.Box(y=y0)\n",
    "box_2 = go.Box(y=y1)\n",
    "data = [heatmap, box_1, box_2]\n",
    "\n",
    "fig = subplots.make_subplots(\n",
    "    rows=2, cols=2, specs=[[{}, {}], [{'colspan': 2}, None]],\n",
    "    subplot_titles=('First Subplot','Second Subplot', 'Third Subplot')\n",
    ")\n",
    "\n",
    "fig.append_trace(box_1, 1, 1)\n",
    "fig.append_trace(box_2, 1, 2)\n",
    "fig.append_trace(heatmap, 2, 1)\n",
    "\n",
    "fig['layout'].update(height=600, width=600, title='i <3 subplots')\n",
    "fig = fig.to_dict()\n",
    "\n",
    "subplot_panel = pn.pane.Plotly(fig)\n",
    "subplot_panel"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Just like in the single-subplot case we can modify just certain aspects of a plot and then trigger an update. E.g. here we replace the overall title text:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "fig['layout']['title']['text'] = 'i <3 updating subplots'\n",
    "subplot_panel.object = fig"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Lastly, Plotly plots can be made responsive using the `autosize` option on a Plotly layout:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import pandas as pd\n",
    "import plotly.express as px\n",
    "\n",
    "data = pd.DataFrame([\n",
    "    ('Monday', 7), ('Tuesday', 4), ('Wednesday', 9), ('Thursday', 4),\n",
    "    ('Friday', 4), ('Saturday', 4), ('Sunay', 4)], columns=['Day', 'Orders']\n",
    ")\n",
    "\n",
    "fig = px.line(data, x=\"Day\", y=\"Orders\")\n",
    "fig.update_traces(mode=\"lines+markers\", marker=dict(size=10), line=dict(width=4))\n",
    "fig.layout.autosize = True\n",
    "\n",
    "responsive = pn.pane.Plotly(fig, config={'responsive': True})\n",
    "\n",
    "pn.Column('# A responsive plot', responsive, sizing_mode='stretch_width')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Controls\n",
    "\n",
    "The `Plotly` pane exposes a number of options which can be changed from both Python and Javascript try out the effect of these parameters interactively:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "pn.Row(responsive.controls(jslink=True), responsive)"
   ]
  }
 ],
 "metadata": {
  "language_info": {
   "name": "python",
   "pygments_lexer": "ipython3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
