{ "cells": [ { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "# Widget Events\n", "\n", "In this lecture we will discuss widget events, such as button clicks!" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Special events" ] }, { "cell_type": "code", "execution_count": 1, "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": 2, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Register a callback to execute when the button is clicked.\n", "\n", " The callback will be called with one argument,\n", " the clicked button widget instance.\n", "\n", " Parameters\n", " ----------\n", " remove : bool (optional)\n", " Set to true to remove the callback from the list of callbacks.\n" ] } ], "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": 3, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Button clicked.\n", "Button clicked.\n", "Button clicked.\n", "Button clicked.\n", "Button clicked.\n", "Button clicked.\n" ] } ], "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_submit" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The `Text` widget also has a special `on_submit` event. The `on_submit` event fires when the user hits return." ] }, { "cell_type": "code", "execution_count": 4, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "hello\n", "press enter\n" ] } ], "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": 5, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Setup a handler to be called when a trait changes.\n", "\n", " This is used to setup dynamic notifications of trait changes.\n", "\n", " Static handlers can be created by creating methods on a HasTraits\n", " subclass with the naming convention '_[traitname]_changed'. Thus,\n", " to create static handler for the trait 'a', create the method\n", " _a_changed(self, name, old, new) (fewer arguments can be used, see\n", " below).\n", "\n", " Parameters\n", " ----------\n", " handler : callable\n", " A callable that is called when a trait changes. Its\n", " signature can be handler(), handler(name), handler(name, new)\n", " or handler(name, old, new).\n", " name : list, str, None\n", " If None, the handler will apply to all traits. If a list\n", " of str, handler will apply to all names in the list. If a\n", " str, the handler will apply just to that name.\n", " remove : bool\n", " If False (the default), then install the handler. If True\n", " then unintall it.\n", " \n" ] } ], "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. " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Linking traitlets attributes from the server side\n", "\n", "The first method is to use the `link` and `dlink` functions from the `traitlets` module. " ] }, { "cell_type": "code", "execution_count": 7, "metadata": { "collapsed": false }, "outputs": [], "source": [ "import traitlets" ] }, { "cell_type": "code", "execution_count": 17, "metadata": { "collapsed": false }, "outputs": [], "source": [ "# Create Caption\n", "caption = widgets.Label(value = 'The values of slider1 and slider2 are synchronized')\n", "\n", "# Create IntSlider\n", "slider1 = widgets.IntSlider(description='Slider 1')\n", "slider2 = widgets.IntSlider(description='Slider 2')\n", "\n", "# Use trailets to link\n", "l = traitlets.link((slider1, 'value'), (slider2, 'value'))\n", "\n", "# Display!\n", "display(caption, slider1, slider2)" ] }, { "cell_type": "code", "execution_count": 16, "metadata": { "collapsed": false }, "outputs": [], "source": [ "# Create Caption\n", "caption = widgets.Label(value = 'Changes in source values are reflected in target1')\n", "\n", "# Create Sliders\n", "source = widgets.IntSlider(description='Source')\n", "target1 = widgets.IntSlider(description='Target 1')\n", "\n", "# Use dlink\n", "dl = traitlets.dlink((source, 'value'), (target1, 'value'))\n", "display(caption, source, target1)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Function `traitlets.link` and `traitlets.dlink` return a `Link` or `DLink` object. The link can be broken by calling the `unlink` method." ] }, { "cell_type": "code", "execution_count": 18, "metadata": { "collapsed": false }, "outputs": [], "source": [ "# May get an error depending on order of cells being run!\n", "l.unlink()\n", "dl.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 due to the roundtrip to the server side. You can also directly link widget attributes in the browser using the link widgets, in either a unidirectional or a bidirectional fashion." ] }, { "cell_type": "code", "execution_count": 19, "metadata": { "collapsed": false }, "outputs": [], "source": [ "# NO LAG VERSION\n", "caption = widgets.Label(value = 'The values of range1 and range2 are synchronized')\n", "\n", "range1 = widgets.IntSlider(description='Range 1')\n", "range2 = widgets.IntSlider(description='Range 2')\n", "\n", "l = widgets.jslink((range1, 'value'), (range2, 'value'))\n", "display(caption, range1, range2)" ] }, { "cell_type": "code", "execution_count": 25, "metadata": { "collapsed": false }, "outputs": [], "source": [ "# NO LAG VERSION\n", "caption = widgets.Label(value = 'Changes in source_range values are reflected in target_range1')\n", "\n", "source_range = widgets.IntSlider(description='Source range')\n", "target_range1 = widgets.IntSlider(description='Target range ')\n", "\n", "dl = widgets.jsdlink((source_range, 'value'), (target_range1, 'value'))\n", "display(caption, source_range, target_range1)" ] }, { "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()\n", "dl.unlink()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Conclusion\n", "You should now feel comfortable linking Widget events!" ] } ], "metadata": { "cell_tags": [ [ "", null ] ], "kernelspec": { "display_name": "Python 2", "language": "python", "name": "python2" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 2 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython2", "version": "2.7.10" } }, "nbformat": 4, "nbformat_minor": 0 }