{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import panel as pn\n",
    "import numpy as np\n",
    "import pandas as pd\n",
    "\n",
    "pn.extension()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The ``Bokeh`` pane allows displaying any displayable [Bokeh](http://bokeh.org) model inside a Panel app. Since Panel is built on Bokeh internally, the Bokeh model is simply inserted into the plot. Since Bokeh models are ordinarily only displayed once, some Panel-related functionality such as syncing multiple views of the same model may not work. Nonetheless this pane type is very useful for combining raw Bokeh code with the higher-level Panel API.\n",
    "\n",
    "When working in a notebook any changes to a Bokeh objects may not be synced automatically requiring an explicit call to `pn.state.push_notebook` with the Panel component containing the Bokeh object.\n",
    "\n",
    "#### Parameters:\n",
    "For the ``theme`` parameter, see the [bokeh themes docs](https://docs.bokeh.org/en/latest/docs/reference/themes.html).\n",
    "\n",
    "* **``object``** (bokeh.layouts.LayoutDOM): The Bokeh model to be displayed\n",
    "* **``theme``** (bokeh.themes.Theme): The Bokeh theme to apply\n",
    "\n",
    "___"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from math import pi\n",
    "\n",
    "from bokeh.palettes import Category20c, Category20\n",
    "from bokeh.plotting import figure\n",
    "from bokeh.transform import cumsum\n",
    "\n",
    "x = {\n",
    "    'United States': 157,\n",
    "    'United Kingdom': 93,\n",
    "    'Japan': 89,\n",
    "    'China': 63,\n",
    "    'Germany': 44,\n",
    "    'India': 42,\n",
    "    'Italy': 40,\n",
    "    'Australia': 35,\n",
    "    'Brazil': 32,\n",
    "    'France': 31,\n",
    "    'Taiwan': 31,\n",
    "    'Spain': 29\n",
    "}\n",
    "\n",
    "data = pd.Series(x).reset_index(name='value').rename(columns={'index':'country'})\n",
    "data['angle'] = data['value']/data['value'].sum() * 2*pi\n",
    "data['color'] = Category20c[len(x)]\n",
    "\n",
    "p = figure(height=350, title=\"Pie Chart\", toolbar_location=None,\n",
    "           tools=\"hover\", tooltips=\"@country: @value\", x_range=(-0.5, 1.0))\n",
    "\n",
    "r = p.wedge(x=0, y=1, radius=0.4,\n",
    "        start_angle=cumsum('angle', include_zero=True), end_angle=cumsum('angle'),\n",
    "        line_color=\"white\", fill_color='color', legend_field='country', source=data)\n",
    "\n",
    "p.axis.axis_label=None\n",
    "p.axis.visible=False\n",
    "p.grid.grid_line_color = None\n",
    "\n",
    "bokeh_pane = pn.pane.Bokeh(p, theme=\"dark_minimal\")\n",
    "bokeh_pane"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "To update a plot with a live server, we can simply modify the underlying model. If we are working in a Jupyter notebook we also have to call the `pn.io.push_notebook` helper function on the component or explicitly trigger an event with `bokeh_pane.param.trigger('object')`:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "r.data_source.data['color'] = Category20[len(x)]\n",
    "pn.io.push_notebook(bokeh_pane)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Alternatively the model may also be replaced entirely, in a live server:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from bokeh.models import Div\n",
    "\n",
    "bokeh_pane.object = Div(text='<h2>This text replaced the pie chart</h2>')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The other nice feature when using Panel to render bokeh objects is that callbacks will work just like they would on the server. So you can simply wrap your existing bokeh app in Panel and it will render and work out of the box both in the notebook and when served as a standalone app:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from bokeh.layouts import column, row\n",
    "from bokeh.models import ColumnDataSource, Slider, TextInput\n",
    "\n",
    "# Set up data\n",
    "N = 200\n",
    "x = np.linspace(0, 4*np.pi, N)\n",
    "y = np.sin(x)\n",
    "source = ColumnDataSource(data=dict(x=x, y=y))\n",
    "\n",
    "# Set up plot\n",
    "plot = figure(height=400, width=400, title=\"my sine wave\",\n",
    "              tools=\"crosshair,pan,reset,save,wheel_zoom\",\n",
    "              x_range=[0, 4*np.pi], y_range=[-2.5, 2.5])\n",
    "\n",
    "plot.line('x', 'y', source=source, line_width=3, line_alpha=0.6)\n",
    "\n",
    "# Set up widgets\n",
    "text = TextInput(title=\"title\", value='my sine wave')\n",
    "offset = Slider(title=\"offset\", value=0.0, start=-5.0, end=5.0, step=0.1)\n",
    "amplitude = Slider(title=\"amplitude\", value=1.0, start=-5.0, end=5.0, step=0.1)\n",
    "phase = Slider(title=\"phase\", value=0.0, start=0.0, end=2*np.pi)\n",
    "freq = Slider(title=\"frequency\", value=1.0, start=0.1, end=5.1, step=0.1)\n",
    "\n",
    "# Set up callbacks\n",
    "def update_title(attrname, old, new):\n",
    "    plot.title.text = text.value\n",
    "\n",
    "text.on_change('value', update_title)\n",
    "\n",
    "def update_data(attrname, old, new):\n",
    "\n",
    "    # Get the current slider values\n",
    "    a = amplitude.value\n",
    "    b = offset.value\n",
    "    w = phase.value\n",
    "    k = freq.value\n",
    "\n",
    "    # Generate the new curve\n",
    "    x = np.linspace(0, 4*np.pi, N)\n",
    "    y = a*np.sin(k*x + w) + b\n",
    "\n",
    "    source.data = dict(x=x, y=y)\n",
    "\n",
    "for w in [offset, amplitude, phase, freq]:\n",
    "    w.on_change('value', update_data)\n",
    "\n",
    "# Set up layouts and add to document\n",
    "inputs = column(text, offset, amplitude, phase, freq)\n",
    "\n",
    "bokeh_app = pn.pane.Bokeh(row(inputs, plot, width=800))\n",
    "\n",
    "bokeh_app"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Controls\n",
    "\n",
    "The `Bokeh` 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(bokeh_app.controls(jslink=True), bokeh_app)"
   ]
  }
 ],
 "metadata": {
  "language_info": {
   "name": "python",
   "pygments_lexer": "ipython3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
