diff --git a/examples/user_guide/Links.ipynb b/examples/user_guide/Links.ipynb new file mode 100644 index 0000000000..3e93e5e561 --- /dev/null +++ b/examples/user_guide/Links.ipynb @@ -0,0 +1,310 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Links parameters in panel" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "In the [param user guide](Param.ipynb), we have seen how parameterized classes are transformed to user interface at free cost. However in certain cases, we need more customisation over widgets automatically generated. Indeed widgets created with `widgets` modules exposed more attributes and allow more possibilities.\n", + "\n", + "In this notebook we will see how to links custom panels between them:\n", + "\n", + " - links between `Parameters` of `Parameterized` classes (most of panel inherit from `param.Parameterized`) with `link` method\n", + " - links between `Parameters` and custom functions with `watch` method" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import param\n", + "import panel as pn\n", + "pn.extension()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Links between `params` of `Parameterized` classes" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "In this example we will link a markdown panel to a text input widget" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "mkdown_pane = pn.pane.Markdown('Markdown display')\n", + "mkdown_pane" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "widget_text = pn.widgets.TextInput(value=mkdown_pane.object)\n", + "widget_text" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We verify both objects we want to link are `param.Parameterized` objects" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "print(isinstance(mkdown_pane, param.Parameterized), isinstance(widget_text, param.Parameterized))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Link of widget `value` attribute to the `object` attribute of the panel" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "widget_text.link(mkdown_pane, value = 'object')\n", + "widget_text.value = 'New text' #above displays should reflect change" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Accessing to available parameters using `params` method" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "widget_text.params()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Link `params` with `watch` method " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Previously we have seen how to link one to one parameters between to Parameterized classes. However value between `parameters` must be the same. Sometimes parameters are linked but values differ. In this case we will add a `watcher`. A `watcher` is a function called when the attribute is changed." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Let's see how to link the disabled property of a `TextInput` widget to a `ToggleButton`" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "widget text creation" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "widget_text = pn.widgets.TextInput(value='text', disabled=True)\n", + "widget_text" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "toggle button creation" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "widget_toggle = pn.widgets.Toggle(active=False, name='Click to enable text')\n", + "widget_toggle" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We could link toggle `active` parameter to the text widget `disabled` parameter with:\n", + "```python\n", + "widget_toggle.link(widget_text, active = 'disabled')\n", + "```\n", + "However we want the opposite behavior, when toggle is `active=True` text input is enabled (`disabled=False`)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "To do it we use the `watch` method of the class `parameters` to connect the toggle button to the text input" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "watcher = widget_toggle.param.watch(lambda change: setattr(widget_text, 'disabled', not change.new), 'active' )" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now cliking on the toggle button enable or disable the text input" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "`param.watch` return a reference to the watcher which can be used to remove it." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "widget_toggle.param.unwatch(watcher)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "the two widgets are not linked anymore" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## More advance usage" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Using the `Sine` example of the [param user guide](Param.ipynb), we will create sliders with a custom step value. Indeed the default step of sliders automatically generated is 0.1 and is not always adapted." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import numpy as np\n", + "\n", + "class Sine(param.Parameterized):\n", + "\n", + " phase = param.Number(default=0, bounds=(0, np.pi))\n", + "\n", + " frequency = param.Number(default=1, bounds=(0.1, 2))\n", + " \n", + " \n", + " def widgets(self):\n", + " phase_params = self.params()['phase']\n", + " frequency_params = self.params()['frequency']\n", + " phase_slider = pn.widgets.FloatSlider(value=phase_params.default,\n", + " start=phase_params.bounds[0],\n", + " end=phase_params.bounds[1], step=1e-2)\n", + " frequency_slider = pn.widgets.FloatSlider(value=frequency_params.default,\n", + " start=frequency_params.bounds[0],\n", + " end=frequency_params.bounds[1], step=1e-2)\n", + " phase_slider.link(self, value='phase')\n", + " frequency_slider.link(self, value='frequency')\n", + " return pn.widgets.WidgetBox(frequency_slider, phase_slider)\n", + "\n", + " @param.depends('phase', 'frequency')\n", + " def view(self):\n", + " y = np.sin(np.linspace(0, np.pi*3, 40)*self.frequency+self.phase)\n", + " y = ((y-y.min())/y.ptp())*20\n", + " array = np.array([list((' '*(int(round(d))-1) + '*').ljust(20)) for d in y])\n", + " return pn.pane.Str('\\n'.join([''.join(r) for r in array.T]), height=325, width=500)\n", + "\n", + "sine = Sine(name='ASCII Sine Wave')\n", + "pn.panel(pn.Row(sine.widgets(), sine.view))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.6.4" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +}