{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import panel as pn\n",
    "import param\n",
    "\n",
    "pn.extension()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The ``panel.Param`` pane allows customizing the widgets, layout and style of the parameters of a `param.Parametrized` Class.\n",
    "\n",
    "#### Parameters:\n",
    "\n",
    "The basic parameters are:\n",
    "\n",
    "* **`object`** (param.parameterized.Parameters): The `param` attribute of a `param.Parameterized` Class\n",
    "* **`parameters`** (List[str]): A list identifying the subset of parameters to include in the Pane.\n",
    "* **`widgets`** (Dict): A Dictionary specifying which parameters and widgets to use for a given parameter. You can also specify widget attributes.\n",
    "\n",
    "The more advanced parameters which give you more control are:\n",
    " \n",
    "* **`default_layout`** (ClassSelector): A layout like Column, Row, etc. or a Custom GridBox.\n",
    "* **`display_threshold`** (float): Parameters with precedence below this value are not displayed.\n",
    "* **`expand`** (bool): Whether parameterized subobjects are expanded or collapsed on instantiation.\n",
    "* **`expand_button`** (bool): Whether to add buttons to expand and collapse sub-objects.\n",
    "* **`expand_layout`** (layout): Layout to expand sub-objects into.\n",
    "* **`name`** (str): The title of the pane.\n",
    "* **`show_labels`** (bool): Whether or not to show labels\n",
    "* **`show_name`** (bool): Whether or not to show the name of the Parameterized Class \n",
    "\n",
    "For more layout and styling related parameters see the [customization user guide](../../user_guide/Customization.ipynb).\n",
    "\n",
    "For an alternative example of using `panel.Param` see the [parameters user guide](../../user_guide/Param.ipynb).\n",
    "___"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Model building\n",
    "Let's build a model of a cycling Athlete and her PowerCurve. \n",
    "\n",
    "The PowerCurve is a recording of her maximum power output in Watt per kg for fixed durations of time."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import datetime\n",
    "import pandas as pd\n",
    "import hvplot.pandas\n",
    "\n",
    "DATE_BOUNDS = (datetime.date(1900, 1, 1), datetime.datetime.now().date())\n",
    "\n",
    "class PowerCurve(param.Parameterized):\n",
    "    ten_sec = param.Number(1079)\n",
    "    ten_sec_date = param.Date(datetime.date(2018, 8, 21), bounds=DATE_BOUNDS)\n",
    "    one_min = param.Number(684)\n",
    "    one_min_date = param.Date(datetime.date(2017, 8, 31), bounds=DATE_BOUNDS)\n",
    "    ten_min = param.Number(419)\n",
    "    ten_min_date = param.Date(datetime.date(2017, 9, 22), bounds=DATE_BOUNDS)\n",
    "    twenty_min = param.Number(398)\n",
    "    twenty_min_date = param.Date(datetime.date(2017, 9, 22), bounds=DATE_BOUNDS)\n",
    "    one_hour = param.Number(319)\n",
    "    one_hour_date = param.Date(datetime.date(2017, 8, 6), bounds=DATE_BOUNDS)\n",
    "    \n",
    "    @param.depends(\"ten_sec\", \"one_min\", \"ten_min\", \"twenty_min\", \"one_hour\")\n",
    "    def plot(self):\n",
    "        data = {\n",
    "            \"duration\": [10 / 60, 1, 10, 20, 60],\n",
    "            \"power\": [self.ten_sec, self.one_min, self.ten_min, self.twenty_min, self.one_hour],\n",
    "        }\n",
    "        dataframe = pd.DataFrame(data)\n",
    "        line_plot = dataframe.hvplot.line(\n",
    "            x=\"duration\", y=\"power\", line_color=\"#007BFF\", line_width=3, responsive=True,\n",
    "        )\n",
    "        scatter_plot = dataframe.hvplot.scatter(\n",
    "            x=\"duration\", y=\"power\", marker=\"o\", size=6, color=\"#007BFF\", responsive=True\n",
    "        )\n",
    "        fig = line_plot * scatter_plot\n",
    "        gridstyle = {\"grid_line_color\": \"black\", \"grid_line_width\": 0.1}\n",
    "        fig = fig.opts(\n",
    "            min_height=400,\n",
    "            toolbar=None,\n",
    "            yticks=list(range(0, 1600, 200)),\n",
    "            ylim=(0, 1500),\n",
    "            gridstyle=gridstyle,\n",
    "            show_grid=True,\n",
    "        )\n",
    "        return fig\n",
    "\n",
    "class Athlete(param.Parameterized):\n",
    "    name_ = param.String(\"P.A. Nelson\")\n",
    "    birthday = param.Date(datetime.date(1976, 9, 17), bounds=DATE_BOUNDS)\n",
    "    weight = param.Number(default=82, bounds=(20,300))\n",
    "    power_curve = param.ClassSelector(class_=PowerCurve, default=PowerCurve())\n",
    "    \n",
    "athlete = Athlete()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The `pn.Param` pane can be used to view and edit the models.\n",
    "\n",
    "Try clicking the `...` button. This will expand the PowerCurve if running in an interactive notebook."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "pn.Param(athlete)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The default Name and Birthday widgets are slow to use. So let's change them to a `DatePicker` and a `LiteralInput`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "pn.Param(athlete, widgets={\"birthday\": pn.widgets.DatePicker, \"weight\": pn.widgets.LiteralInput})"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Let's expand the power curve by default:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "pn.Param(\n",
    "    athlete, \n",
    "    widgets={\n",
    "        \"birthday\": pn.widgets.DatePicker, \n",
    "        \"weight\": pn.widgets.LiteralInput\n",
    "    }, expand=True)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Now let's try to display the Name and Birthday only and in a Row."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "pn.Param(\n",
    "    athlete,\n",
    "    widgets={\"birthday\": pn.widgets.DatePicker},\n",
    "    parameters=[\"name_\", \"birthday\"],\n",
    "    show_name=False,\n",
    "    default_layout=pn.Row,\n",
    "    width=400\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Let's customize the view of the `Athlete` some more."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "athlete_view = pn.Param(\n",
    "    athlete,\n",
    "    widgets={\n",
    "        \"birthday\": pn.widgets.DatePicker, \n",
    "        \"weight\": {\"type\": pn.widgets.LiteralInput, \"width\": 100}\n",
    "    },\n",
    "    parameters=[\"name_\", \"birthday\", \"weight\"],\n",
    "    show_name=False,\n",
    "    default_layout=pn.Row,\n",
    "    width=600\n",
    ")\n",
    "athlete_view"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Let's take a look at the PowerCurve."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "pn.Param(athlete.power_curve)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The PowerCurve layout is not that tidy. Let's change the layout to two columns."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def new_class(cls, **kwargs):\n",
    "    \"Creates a new class which overrides parameter defaults.\"\n",
    "    return type(type(cls).__name__, (cls,), kwargs)\n",
    "\n",
    "power_curve_columns_view = pn.Param(\n",
    "    athlete.power_curve,\n",
    "    default_layout=new_class(pn.GridBox, ncols=2),\n",
    "    show_name=False,\n",
    "    widgets = {\n",
    "        \"ten_sec_date\": pn.widgets.DatePicker, \n",
    "        \"one_min_date\": pn.widgets.DatePicker, \n",
    "        \"ten_min_date\": pn.widgets.DatePicker,\n",
    "        \"twenty_min_date\": pn.widgets.DatePicker, \n",
    "        \"one_hour_date\": pn.widgets.DatePicker\n",
    "    }\n",
    ")\n",
    "\n",
    "power_curve_columns_view"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Let's put a plot of the PowerCurve in the mix."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "sections = {'InputFiles': ['dirs', 'files'], 'Field': ['grps', 'varns']}\n",
    "\n",
    "def update(target, event):\n",
    "    target.set_param(options=sections[event.new], value=sections[event.new][0])\n",
    "\n",
    "sel = pn.widgets.Select(options=list(sections.keys()))\n",
    "rad = pn.widgets.RadioButtonGroup(options=sections[sel.value])\n",
    "sel.link(rad, callbacks={'value': update})\n",
    "\n",
    "pn.Column(sel, rad)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "power_curve_view = pn.Row(\n",
    "    power_curve_columns_view,\n",
    "    pn.layout.VSpacer(width=50),\n",
    "    athlete.power_curve.plot\n",
    ")\n",
    "power_curve_view"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "And finally, let's put things together."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "pn.Column(\n",
    "    pn.pane.Markdown(\"### Athlete\"), \n",
    "    athlete_view, \n",
    "    pn.pane.Markdown(\"#### Power Curve\"), \n",
    "    power_curve_view,\n",
    "    sizing_mode='stretch_width'\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Disabling continuous updates for slider widgets\n",
    "When a function takes a long time to run and depends on a parameter, realtime feedback can be a burden instead of being helpful.\n",
    "\n",
    "Therefore if a slider widget is used for a parameter and you have a function which takes long time to calculate, you can set the `throttled` keyword in the `panel.param.widgets` to `True` for the given parameter. This will then first run the function after the release of the mouse button of the slider.\n",
    "\n",
    "An example can be seen below where two parameters is connected to two sliders, one with and one without `throttled` enabled."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "class A(param.Parameterized):\n",
    "    without_throttled_enabled = param.Range(\n",
    "        default=(100, 250),\n",
    "        bounds=(0, 250),\n",
    "    )\n",
    "\n",
    "    with_throttled_enabled = param.Range(\n",
    "        default=(100, 250),\n",
    "        bounds=(0, 250),\n",
    "    )\n",
    "\n",
    "    def __init__(self, **params):\n",
    "        super().__init__(**params)\n",
    "        widgets = {\n",
    "            \"without_throttled_enabled\": pn.widgets.IntRangeSlider,\n",
    "            \"with_throttled_enabled\": {\n",
    "                \"type\": pn.widgets.IntRangeSlider,\n",
    "                \"throttled\": True,\n",
    "            },\n",
    "        }\n",
    "        self.controls = pn.Param(self, widgets=widgets)\n",
    "\n",
    "    @param.depends(\"controls\")\n",
    "    def calculation(self):\n",
    "        return self.without_throttled_enabled, self.with_throttled_enabled\n",
    "\n",
    "\n",
    "a = A()\n",
    "pn.Column(a.controls, a.calculation)"
   ]
  }
 ],
 "metadata": {
  "language_info": {
   "name": "python",
   "pygments_lexer": "ipython3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
