from __future__ import print_function

"""
Explanation: Index - Back - Next
Widget Events
Special events
End of explanation
"""


import ipywidgets as widgets
print(widgets.Button.on_click.__doc__)

"""
Explanation: 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.
End of explanation
"""


from IPython.display import display
button = widgets.Button(description="Click Me!")
display(button)

def on_button_clicked(b):
    print("Button clicked.")

button.on_click(on_button_clicked)

"""
Explanation: Example
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.
End of explanation
"""


text = widgets.Text()
display(text)

def handle_submit(sender):
    print(text.value)

text.on_submit(handle_submit)

"""
Explanation: on_submit
The Text widget also has a special on_submit event.  The on_submit event fires when the user hits return.
End of explanation
"""


print(widgets.Widget.observe.__doc__)

"""
Explanation: Traitlet events
Widget properties are IPython traitlets and traitlets are eventful.  To handle  changes, the observe method of the widget can be used to register a callback.  The doc string for observe can be seen below.
End of explanation
"""


int_range = widgets.IntSlider()
display(int_range)

def on_value_change(change):
    print(change['new'])

int_range.observe(on_value_change, names='value')

"""
Explanation: Signatures
Mentioned in the doc string, the callback registered must have the signature handler(change) where change is a dictionary holding the information about the change. 
Using this method, an example of how to output an IntSlider's value as it is changed can be seen below.
End of explanation
"""


import traitlets

caption = widgets.Label(value='The values of slider1 and slider2 are synchronized')
sliders1, slider2 = widgets.IntSlider(description='Slider 1'),\
                    widgets.IntSlider(description='Slider 2')
l = traitlets.link((sliders1, 'value'), (slider2, 'value'))
display(caption, sliders1, slider2)

caption = widgets.Label(value='Changes in source values are reflected in target1')
source, target1 = widgets.IntSlider(description='Source'),\
                  widgets.IntSlider(description='Target 1')
dl = traitlets.dlink((source, 'value'), (target1, 'value'))
display(caption, source, target1)

"""
Explanation: Linking Widgets
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. 
Linking traitlets attributes in the kernel
The first method is to use the link and dlink functions from the traitlets module. This only works if we are interacting with a live kernel.
End of explanation
"""


l.unlink()
dl.unlink()

"""
Explanation: Function traitlets.link and traitlets.dlink return a Link or DLink object. The link can be broken by calling the unlink method.
End of explanation
"""


caption = widgets.Label(value='The values of range1 and range2 are synchronized')
slider = widgets.IntSlider(min=-5, max=5, value=1, description='Slider')

def handle_slider_change(change):
    caption.value = 'The slider value is ' + (
        'negative' if change.new < 0 else 'nonnegative'
    )

slider.observe(handle_slider_change, names='value')

display(caption, slider)

"""
Explanation: Registering callbacks to trait changes in the kernel
Since attributes of widgets on the Python side are traitlets, you can register handlers to the change events whenever the model gets updates from the front-end.
The handler passed to the decorator will be called with one change argument. The change objecy at least holds a type key and a name key, corresponding respectively to the type of notification and the name of the attribute that triggered the notification.
Other keys may be passed depending on the value of type. In the case where type is change, we also have the following keys:

owner : the HasTraits instance
old : the old value of the modified trait attribute
new : the new value of the modified trait attribute
name : the name of the modified trait attribute.
End of explanation
"""


caption = widgets.Label(value='The values of range1 and range2 are synchronized')
range1, range2 = widgets.IntSlider(description='Range 1'),\
                 widgets.IntSlider(description='Range 2')
l = widgets.jslink((range1, 'value'), (range2, 'value'))
display(caption, range1, range2)

caption = widgets.Label(value='Changes in source_range values are reflected in target_range1')
source_range, target_range1 = widgets.IntSlider(description='Source range'),\
                              widgets.IntSlider(description='Target range 1')
dl = widgets.jsdlink((source_range, 'value'), (target_range1, 'value'))
display(caption, source_range, target_range1)

"""
Explanation: Linking widgets attributes from the client side
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.
Javascript links persist when embedding widgets in html web pages without a kernel.
End of explanation
"""


# l.unlink()
# dl.unlink()

"""
Explanation: Function widgets.jslink returns a Link widget. The link can be broken by calling the unlink method.
End of explanation
"""


