{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "[Index](Index.ipynb) - [Back](Widget%20List.ipynb) - [Next](Widget Styling.ipynb)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "# Widget Events"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Special events"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "from __future__ import print_function"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The `Button` is not used to represent a data type.  Instead the button widget is used to **handle mouse clicks**.  The **`on_click` method** of the `Button` can be used to register function to be called when the button is clicked.  The doc string of the `on_click` can be seen below."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "import ipywidgets as widgets\n",
    "print(widgets.Button.on_click.__doc__)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "### Example"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Since button clicks are **stateless**, they are **transmitted from the front-end to the back-end using custom messages**.  By using the `on_click` method, a button that prints a message when it has been clicked is shown below."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "from IPython.display import display\n",
    "button = widgets.Button(description=\"Click Me!\")\n",
    "display(button)\n",
    "\n",
    "def on_button_clicked(b):\n",
    "    print(\"Button clicked.\")\n",
    "\n",
    "button.on_click(on_button_clicked)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "### on_sumbit"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The **`Text`** also has a special **`on_submit` event**.  The `on_submit` event **fires when the user hits return**."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "text = widgets.Text()\n",
    "display(text)\n",
    "\n",
    "def handle_submit(sender):\n",
    "    print(text.value)\n",
    "\n",
    "text.on_submit(handle_submit)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "## Traitlet events"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Widget properties are IPython traitlets** and **traitlets are eventful**.  To handle  changes, the **`on_trait_change` method** of the widget can be used to **register a callback**.  The doc string for `on_trait_change` can be seen below."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "print(widgets.Widget.on_trait_change.__doc__)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "### Signatures"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Mentioned in the doc string, the callback registered can have **4 possible signatures**:\n",
    "\n",
    "- callback()\n",
    "- callback(trait_name)\n",
    "- callback(trait_name, new_value)\n",
    "- callback(trait_name, old_value, new_value)\n",
    "\n",
    "Using this method, an example of how to output an `IntSlider`'s value as it is changed can be seen below."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "int_range = widgets.IntSlider()\n",
    "display(int_range)\n",
    "\n",
    "def on_value_change(name, value):\n",
    "    print(value)\n",
    "\n",
    "int_range.on_trait_change(on_value_change, 'value')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Linking Widgets"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Often, you may want to simply link widget attributes together. Synchronization of attributes can be done in a simpler way than by using bare traitlets events. \n",
    "\n",
    "The first method is to use the `link` and `directional_link` functions from the `traitlets` module. "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Linking traitlets attributes from the server side"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "import traitlets"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "caption = widgets.Latex(value = 'The values of slider1, slider2 and slider3 are synchronized')\n",
    "sliders1, slider2, slider3 = widgets.IntSlider(description='Slider 1'),\\\n",
    "                             widgets.IntSlider(description='Slider 2'),\\\n",
    "                             widgets.IntSlider(description='Slider 3')\n",
    "l = traitlets.link((sliders1, 'value'), (slider2, 'value'), (slider3, 'value'))\n",
    "display(caption, sliders1, slider2, slider3)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "caption = widgets.Latex(value = 'Changes in source values are reflected in target1 and target2')\n",
    "source, target1, target2 = widgets.IntSlider(description='Source'),\\\n",
    "                           widgets.IntSlider(description='Target 1'),\\\n",
    "                           widgets.IntSlider(description='Target 2')\n",
    "traitlets.dlink((source, 'value'), (target1, 'value'), (target2, 'value'))\n",
    "display(caption, source, target1, target2)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Function `traitlets.link` returns a `Link` object. The link can be broken by calling the `unlink` method."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "# l.unlink()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Linking widgets attributes from the client side"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "When synchronizing traitlets attributes, you may experience a lag because of the latency dues to the rountrip to the server side. You can also directly link widgets attributes, either in a unidirectional or a bidirectional fashion using the link widgets. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "caption = widgets.Latex(value = 'The values of range1, range2 and range3 are synchronized')\n",
    "range1, range2, range3 = widgets.IntSlider(description='Range 1'),\\\n",
    "                         widgets.IntSlider(description='Range 2'),\\\n",
    "                         widgets.IntSlider(description='Range 3')\n",
    "l = widgets.jslink((range1, 'value'), (range2, 'value'), (range3, 'value'))\n",
    "display(caption, range1, range2, range3)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "caption = widgets.Latex(value = 'Changes in source_range values are reflected in target_range1 and target_range2')\n",
    "source_range, target_range1, target_range2 = widgets.IntSlider(description='Source range'),\\\n",
    "                                             widgets.IntSlider(description='Target range 1'),\\\n",
    "                                             widgets.IntSlider(description='Target range 2')\n",
    "widgets.jsdlink((source_range, 'value'), (target_range1, 'value'), (target_range2, 'value'))\n",
    "display(caption, source_range, target_range1, target_range2)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Function `widgets.jslink` returns a `Link` widget. The link can be broken by calling the `unlink` method."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "# l.unlink()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "[Index](Index.ipynb) - [Back](Widget%20List.ipynb) - [Next](Widget Styling.ipynb)"
   ]
  }
 ],
 "metadata": {
  "cell_tags": [
   [
    "<None>",
    null
   ]
  ],
  "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.4.2"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 0
}
