Uploaded Test files
This commit is contained in:
parent
f584ad9d97
commit
2e81cb7d99
16627 changed files with 2065359 additions and 102444 deletions
576
venv/Lib/site-packages/ipywidgets/widgets/interaction.py
Normal file
576
venv/Lib/site-packages/ipywidgets/widgets/interaction.py
Normal file
|
|
@ -0,0 +1,576 @@
|
|||
# Copyright (c) Jupyter Development Team.
|
||||
# Distributed under the terms of the Modified BSD License.
|
||||
|
||||
"""Interact with functions using widgets."""
|
||||
|
||||
from __future__ import print_function
|
||||
from __future__ import division
|
||||
|
||||
try: # Python >= 3.3
|
||||
from inspect import signature, Parameter
|
||||
except ImportError:
|
||||
from IPython.utils.signatures import signature, Parameter
|
||||
from inspect import getcallargs
|
||||
|
||||
try:
|
||||
from inspect import getfullargspec as check_argspec
|
||||
except ImportError:
|
||||
from inspect import getargspec as check_argspec # py2
|
||||
import sys
|
||||
|
||||
from IPython.core.getipython import get_ipython
|
||||
from . import (ValueWidget, Text,
|
||||
FloatSlider, IntSlider, Checkbox, Dropdown,
|
||||
VBox, Button, DOMWidget, Output)
|
||||
from IPython.display import display, clear_output
|
||||
from ipython_genutils.py3compat import string_types, unicode_type
|
||||
from traitlets import HasTraits, Any, Unicode, observe
|
||||
from numbers import Real, Integral
|
||||
from warnings import warn
|
||||
|
||||
try:
|
||||
from collections.abc import Iterable, Mapping
|
||||
except ImportError:
|
||||
from collections import Iterable, Mapping # py2
|
||||
|
||||
|
||||
empty = Parameter.empty
|
||||
|
||||
|
||||
def show_inline_matplotlib_plots():
|
||||
"""Show matplotlib plots immediately if using the inline backend.
|
||||
|
||||
With ipywidgets 6.0, matplotlib plots don't work well with interact when
|
||||
using the inline backend that comes with ipykernel. Basically, the inline
|
||||
backend only shows the plot after the entire cell executes, which does not
|
||||
play well with drawing plots inside of an interact function. See
|
||||
https://github.com/jupyter-widgets/ipywidgets/issues/1181/ and
|
||||
https://github.com/ipython/ipython/issues/10376 for more details. This
|
||||
function displays any matplotlib plots if the backend is the inline backend.
|
||||
"""
|
||||
if 'matplotlib' not in sys.modules:
|
||||
# matplotlib hasn't been imported, nothing to do.
|
||||
return
|
||||
|
||||
try:
|
||||
import matplotlib as mpl
|
||||
from ipykernel.pylab.backend_inline import flush_figures
|
||||
except ImportError:
|
||||
return
|
||||
|
||||
if mpl.get_backend() == 'module://ipykernel.pylab.backend_inline':
|
||||
flush_figures()
|
||||
|
||||
|
||||
def interactive_output(f, controls):
|
||||
"""Connect widget controls to a function.
|
||||
|
||||
This function does not generate a user interface for the widgets (unlike `interact`).
|
||||
This enables customisation of the widget user interface layout.
|
||||
The user interface layout must be defined and displayed manually.
|
||||
"""
|
||||
|
||||
out = Output()
|
||||
def observer(change):
|
||||
kwargs = {k:v.value for k,v in controls.items()}
|
||||
show_inline_matplotlib_plots()
|
||||
with out:
|
||||
clear_output(wait=True)
|
||||
f(**kwargs)
|
||||
show_inline_matplotlib_plots()
|
||||
for k,w in controls.items():
|
||||
w.observe(observer, 'value')
|
||||
show_inline_matplotlib_plots()
|
||||
observer(None)
|
||||
return out
|
||||
|
||||
|
||||
def _matches(o, pattern):
|
||||
"""Match a pattern of types in a sequence."""
|
||||
if not len(o) == len(pattern):
|
||||
return False
|
||||
comps = zip(o,pattern)
|
||||
return all(isinstance(obj,kind) for obj,kind in comps)
|
||||
|
||||
|
||||
def _get_min_max_value(min, max, value=None, step=None):
|
||||
"""Return min, max, value given input values with possible None."""
|
||||
# Either min and max need to be given, or value needs to be given
|
||||
if value is None:
|
||||
if min is None or max is None:
|
||||
raise ValueError('unable to infer range, value from: ({0}, {1}, {2})'.format(min, max, value))
|
||||
diff = max - min
|
||||
value = min + (diff / 2)
|
||||
# Ensure that value has the same type as diff
|
||||
if not isinstance(value, type(diff)):
|
||||
value = min + (diff // 2)
|
||||
else: # value is not None
|
||||
if not isinstance(value, Real):
|
||||
raise TypeError('expected a real number, got: %r' % value)
|
||||
# Infer min/max from value
|
||||
if value == 0:
|
||||
# This gives (0, 1) of the correct type
|
||||
vrange = (value, value + 1)
|
||||
elif value > 0:
|
||||
vrange = (-value, 3*value)
|
||||
else:
|
||||
vrange = (3*value, -value)
|
||||
if min is None:
|
||||
min = vrange[0]
|
||||
if max is None:
|
||||
max = vrange[1]
|
||||
if step is not None:
|
||||
# ensure value is on a step
|
||||
tick = int((value - min) / step)
|
||||
value = min + tick * step
|
||||
if not min <= value <= max:
|
||||
raise ValueError('value must be between min and max (min={0}, value={1}, max={2})'.format(min, value, max))
|
||||
return min, max, value
|
||||
|
||||
def _yield_abbreviations_for_parameter(param, kwargs):
|
||||
"""Get an abbreviation for a function parameter."""
|
||||
name = param.name
|
||||
kind = param.kind
|
||||
ann = param.annotation
|
||||
default = param.default
|
||||
not_found = (name, empty, empty)
|
||||
if kind in (Parameter.POSITIONAL_OR_KEYWORD, Parameter.KEYWORD_ONLY):
|
||||
if name in kwargs:
|
||||
value = kwargs.pop(name)
|
||||
elif ann is not empty:
|
||||
warn("Using function annotations to implicitly specify interactive controls is deprecated. Use an explicit keyword argument for the parameter instead.", DeprecationWarning)
|
||||
value = ann
|
||||
elif default is not empty:
|
||||
value = default
|
||||
else:
|
||||
yield not_found
|
||||
yield (name, value, default)
|
||||
elif kind == Parameter.VAR_KEYWORD:
|
||||
# In this case name=kwargs and we yield the items in kwargs with their keys.
|
||||
for k, v in kwargs.copy().items():
|
||||
kwargs.pop(k)
|
||||
yield k, v, empty
|
||||
|
||||
|
||||
class interactive(VBox):
|
||||
"""
|
||||
A VBox container containing a group of interactive widgets tied to a
|
||||
function.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
__interact_f : function
|
||||
The function to which the interactive widgets are tied. The `**kwargs`
|
||||
should match the function signature.
|
||||
__options : dict
|
||||
A dict of options. Currently, the only supported keys are
|
||||
``"manual"`` and ``"manual_name"``.
|
||||
**kwargs : various, optional
|
||||
An interactive widget is created for each keyword argument that is a
|
||||
valid widget abbreviation.
|
||||
|
||||
Note that the first two parameters intentionally start with a double
|
||||
underscore to avoid being mixed up with keyword arguments passed by
|
||||
``**kwargs``.
|
||||
"""
|
||||
def __init__(self, __interact_f, __options={}, **kwargs):
|
||||
VBox.__init__(self, _dom_classes=['widget-interact'])
|
||||
self.result = None
|
||||
self.args = []
|
||||
self.kwargs = {}
|
||||
|
||||
self.f = f = __interact_f
|
||||
self.clear_output = kwargs.pop('clear_output', True)
|
||||
self.manual = __options.get("manual", False)
|
||||
self.manual_name = __options.get("manual_name", "Run Interact")
|
||||
self.auto_display = __options.get("auto_display", False)
|
||||
|
||||
new_kwargs = self.find_abbreviations(kwargs)
|
||||
# Before we proceed, let's make sure that the user has passed a set of args+kwargs
|
||||
# that will lead to a valid call of the function. This protects against unspecified
|
||||
# and doubly-specified arguments.
|
||||
try:
|
||||
check_argspec(f)
|
||||
except TypeError:
|
||||
# if we can't inspect, we can't validate
|
||||
pass
|
||||
else:
|
||||
getcallargs(f, **{n:v for n,v,_ in new_kwargs})
|
||||
# Now build the widgets from the abbreviations.
|
||||
self.kwargs_widgets = self.widgets_from_abbreviations(new_kwargs)
|
||||
|
||||
# This has to be done as an assignment, not using self.children.append,
|
||||
# so that traitlets notices the update. We skip any objects (such as fixed) that
|
||||
# are not DOMWidgets.
|
||||
c = [w for w in self.kwargs_widgets if isinstance(w, DOMWidget)]
|
||||
|
||||
# If we are only to run the function on demand, add a button to request this.
|
||||
if self.manual:
|
||||
self.manual_button = Button(description=self.manual_name)
|
||||
c.append(self.manual_button)
|
||||
|
||||
self.out = Output()
|
||||
c.append(self.out)
|
||||
self.children = c
|
||||
|
||||
# Wire up the widgets
|
||||
# If we are doing manual running, the callback is only triggered by the button
|
||||
# Otherwise, it is triggered for every trait change received
|
||||
# On-demand running also suppresses running the function with the initial parameters
|
||||
if self.manual:
|
||||
self.manual_button.on_click(self.update)
|
||||
|
||||
# Also register input handlers on text areas, so the user can hit return to
|
||||
# invoke execution.
|
||||
for w in self.kwargs_widgets:
|
||||
if isinstance(w, Text):
|
||||
w.on_submit(self.update)
|
||||
else:
|
||||
for widget in self.kwargs_widgets:
|
||||
widget.observe(self.update, names='value')
|
||||
|
||||
self.on_displayed(self.update)
|
||||
|
||||
# Callback function
|
||||
def update(self, *args):
|
||||
"""
|
||||
Call the interact function and update the output widget with
|
||||
the result of the function call.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
*args : ignored
|
||||
Required for this method to be used as traitlets callback.
|
||||
"""
|
||||
self.kwargs = {}
|
||||
if self.manual:
|
||||
self.manual_button.disabled = True
|
||||
try:
|
||||
show_inline_matplotlib_plots()
|
||||
with self.out:
|
||||
if self.clear_output:
|
||||
clear_output(wait=True)
|
||||
for widget in self.kwargs_widgets:
|
||||
value = widget.get_interact_value()
|
||||
self.kwargs[widget._kwarg] = value
|
||||
self.result = self.f(**self.kwargs)
|
||||
show_inline_matplotlib_plots()
|
||||
if self.auto_display and self.result is not None:
|
||||
display(self.result)
|
||||
except Exception as e:
|
||||
ip = get_ipython()
|
||||
if ip is None:
|
||||
self.log.warn("Exception in interact callback: %s", e, exc_info=True)
|
||||
else:
|
||||
ip.showtraceback()
|
||||
finally:
|
||||
if self.manual:
|
||||
self.manual_button.disabled = False
|
||||
|
||||
# Find abbreviations
|
||||
def signature(self):
|
||||
return signature(self.f)
|
||||
|
||||
def find_abbreviations(self, kwargs):
|
||||
"""Find the abbreviations for the given function and kwargs.
|
||||
Return (name, abbrev, default) tuples.
|
||||
"""
|
||||
new_kwargs = []
|
||||
try:
|
||||
sig = self.signature()
|
||||
except (ValueError, TypeError):
|
||||
# can't inspect, no info from function; only use kwargs
|
||||
return [ (key, value, value) for key, value in kwargs.items() ]
|
||||
|
||||
for param in sig.parameters.values():
|
||||
for name, value, default in _yield_abbreviations_for_parameter(param, kwargs):
|
||||
if value is empty:
|
||||
raise ValueError('cannot find widget or abbreviation for argument: {!r}'.format(name))
|
||||
new_kwargs.append((name, value, default))
|
||||
return new_kwargs
|
||||
|
||||
# Abbreviations to widgets
|
||||
def widgets_from_abbreviations(self, seq):
|
||||
"""Given a sequence of (name, abbrev, default) tuples, return a sequence of Widgets."""
|
||||
result = []
|
||||
for name, abbrev, default in seq:
|
||||
widget = self.widget_from_abbrev(abbrev, default)
|
||||
if not (isinstance(widget, ValueWidget) or isinstance(widget, fixed)):
|
||||
if widget is None:
|
||||
raise ValueError("{!r} cannot be transformed to a widget".format(abbrev))
|
||||
else:
|
||||
raise TypeError("{!r} is not a ValueWidget".format(widget))
|
||||
if not widget.description:
|
||||
widget.description = name
|
||||
widget._kwarg = name
|
||||
result.append(widget)
|
||||
return result
|
||||
|
||||
@classmethod
|
||||
def widget_from_abbrev(cls, abbrev, default=empty):
|
||||
"""Build a ValueWidget instance given an abbreviation or Widget."""
|
||||
if isinstance(abbrev, ValueWidget) or isinstance(abbrev, fixed):
|
||||
return abbrev
|
||||
|
||||
if isinstance(abbrev, tuple):
|
||||
widget = cls.widget_from_tuple(abbrev)
|
||||
if default is not empty:
|
||||
try:
|
||||
widget.value = default
|
||||
except Exception:
|
||||
# ignore failure to set default
|
||||
pass
|
||||
return widget
|
||||
|
||||
# Try single value
|
||||
widget = cls.widget_from_single_value(abbrev)
|
||||
if widget is not None:
|
||||
return widget
|
||||
|
||||
# Something iterable (list, dict, generator, ...). Note that str and
|
||||
# tuple should be handled before, that is why we check this case last.
|
||||
if isinstance(abbrev, Iterable):
|
||||
widget = cls.widget_from_iterable(abbrev)
|
||||
if default is not empty:
|
||||
try:
|
||||
widget.value = default
|
||||
except Exception:
|
||||
# ignore failure to set default
|
||||
pass
|
||||
return widget
|
||||
|
||||
# No idea...
|
||||
return None
|
||||
|
||||
@staticmethod
|
||||
def widget_from_single_value(o):
|
||||
"""Make widgets from single values, which can be used as parameter defaults."""
|
||||
if isinstance(o, string_types):
|
||||
return Text(value=unicode_type(o))
|
||||
elif isinstance(o, bool):
|
||||
return Checkbox(value=o)
|
||||
elif isinstance(o, Integral):
|
||||
min, max, value = _get_min_max_value(None, None, o)
|
||||
return IntSlider(value=o, min=min, max=max)
|
||||
elif isinstance(o, Real):
|
||||
min, max, value = _get_min_max_value(None, None, o)
|
||||
return FloatSlider(value=o, min=min, max=max)
|
||||
else:
|
||||
return None
|
||||
|
||||
@staticmethod
|
||||
def widget_from_tuple(o):
|
||||
"""Make widgets from a tuple abbreviation."""
|
||||
if _matches(o, (Real, Real)):
|
||||
min, max, value = _get_min_max_value(o[0], o[1])
|
||||
if all(isinstance(_, Integral) for _ in o):
|
||||
cls = IntSlider
|
||||
else:
|
||||
cls = FloatSlider
|
||||
return cls(value=value, min=min, max=max)
|
||||
elif _matches(o, (Real, Real, Real)):
|
||||
step = o[2]
|
||||
if step <= 0:
|
||||
raise ValueError("step must be >= 0, not %r" % step)
|
||||
min, max, value = _get_min_max_value(o[0], o[1], step=step)
|
||||
if all(isinstance(_, Integral) for _ in o):
|
||||
cls = IntSlider
|
||||
else:
|
||||
cls = FloatSlider
|
||||
return cls(value=value, min=min, max=max, step=step)
|
||||
|
||||
@staticmethod
|
||||
def widget_from_iterable(o):
|
||||
"""Make widgets from an iterable. This should not be done for
|
||||
a string or tuple."""
|
||||
# Dropdown expects a dict or list, so we convert an arbitrary
|
||||
# iterable to either of those.
|
||||
if isinstance(o, (list, dict)):
|
||||
return Dropdown(options=o)
|
||||
elif isinstance(o, Mapping):
|
||||
return Dropdown(options=list(o.items()))
|
||||
else:
|
||||
return Dropdown(options=list(o))
|
||||
|
||||
# Return a factory for interactive functions
|
||||
@classmethod
|
||||
def factory(cls):
|
||||
options = dict(manual=False, auto_display=True, manual_name="Run Interact")
|
||||
return _InteractFactory(cls, options)
|
||||
|
||||
|
||||
class _InteractFactory(object):
|
||||
"""
|
||||
Factory for instances of :class:`interactive`.
|
||||
|
||||
This class is needed to support options like::
|
||||
|
||||
>>> @interact.options(manual=True)
|
||||
... def greeting(text="World"):
|
||||
... print("Hello {}".format(text))
|
||||
|
||||
Parameters
|
||||
----------
|
||||
cls : class
|
||||
The subclass of :class:`interactive` to construct.
|
||||
options : dict
|
||||
A dict of options used to construct the interactive
|
||||
function. By default, this is returned by
|
||||
``cls.default_options()``.
|
||||
kwargs : dict
|
||||
A dict of **kwargs to use for widgets.
|
||||
"""
|
||||
def __init__(self, cls, options, kwargs={}):
|
||||
self.cls = cls
|
||||
self.opts = options
|
||||
self.kwargs = kwargs
|
||||
|
||||
def widget(self, f):
|
||||
"""
|
||||
Return an interactive function widget for the given function.
|
||||
|
||||
The widget is only constructed, not displayed nor attached to
|
||||
the function.
|
||||
|
||||
Returns
|
||||
-------
|
||||
An instance of ``self.cls`` (typically :class:`interactive`).
|
||||
|
||||
Parameters
|
||||
----------
|
||||
f : function
|
||||
The function to which the interactive widgets are tied.
|
||||
"""
|
||||
return self.cls(f, self.opts, **self.kwargs)
|
||||
|
||||
def __call__(self, __interact_f=None, **kwargs):
|
||||
"""
|
||||
Make the given function interactive by adding and displaying
|
||||
the corresponding :class:`interactive` widget.
|
||||
|
||||
Expects the first argument to be a function. Parameters to this
|
||||
function are widget abbreviations passed in as keyword arguments
|
||||
(``**kwargs``). Can be used as a decorator (see examples).
|
||||
|
||||
Returns
|
||||
-------
|
||||
f : __interact_f with interactive widget attached to it.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
__interact_f : function
|
||||
The function to which the interactive widgets are tied. The `**kwargs`
|
||||
should match the function signature. Passed to :func:`interactive()`
|
||||
**kwargs : various, optional
|
||||
An interactive widget is created for each keyword argument that is a
|
||||
valid widget abbreviation. Passed to :func:`interactive()`
|
||||
|
||||
Examples
|
||||
--------
|
||||
Render an interactive text field that shows the greeting with the passed in
|
||||
text::
|
||||
|
||||
# 1. Using interact as a function
|
||||
def greeting(text="World"):
|
||||
print("Hello {}".format(text))
|
||||
interact(greeting, text="IPython Widgets")
|
||||
|
||||
# 2. Using interact as a decorator
|
||||
@interact
|
||||
def greeting(text="World"):
|
||||
print("Hello {}".format(text))
|
||||
|
||||
# 3. Using interact as a decorator with named parameters
|
||||
@interact(text="IPython Widgets")
|
||||
def greeting(text="World"):
|
||||
print("Hello {}".format(text))
|
||||
|
||||
Render an interactive slider widget and prints square of number::
|
||||
|
||||
# 1. Using interact as a function
|
||||
def square(num=1):
|
||||
print("{} squared is {}".format(num, num*num))
|
||||
interact(square, num=5)
|
||||
|
||||
# 2. Using interact as a decorator
|
||||
@interact
|
||||
def square(num=2):
|
||||
print("{} squared is {}".format(num, num*num))
|
||||
|
||||
# 3. Using interact as a decorator with named parameters
|
||||
@interact(num=5)
|
||||
def square(num=2):
|
||||
print("{} squared is {}".format(num, num*num))
|
||||
"""
|
||||
# If kwargs are given, replace self by a new
|
||||
# _InteractFactory with the updated kwargs
|
||||
if kwargs:
|
||||
kw = dict(self.kwargs)
|
||||
kw.update(kwargs)
|
||||
self = type(self)(self.cls, self.opts, kw)
|
||||
|
||||
f = __interact_f
|
||||
if f is None:
|
||||
# This branch handles the case 3
|
||||
# @interact(a=30, b=40)
|
||||
# def f(*args, **kwargs):
|
||||
# ...
|
||||
#
|
||||
# Simply return the new factory
|
||||
return self
|
||||
|
||||
# positional arg support in: https://gist.github.com/8851331
|
||||
# Handle the cases 1 and 2
|
||||
# 1. interact(f, **kwargs)
|
||||
# 2. @interact
|
||||
# def f(*args, **kwargs):
|
||||
# ...
|
||||
w = self.widget(f)
|
||||
try:
|
||||
f.widget = w
|
||||
except AttributeError:
|
||||
# some things (instancemethods) can't have attributes attached,
|
||||
# so wrap in a lambda
|
||||
f = lambda *args, **kwargs: __interact_f(*args, **kwargs)
|
||||
f.widget = w
|
||||
show_inline_matplotlib_plots()
|
||||
display(w)
|
||||
return f
|
||||
|
||||
def options(self, **kwds):
|
||||
"""
|
||||
Change options for interactive functions.
|
||||
|
||||
Returns
|
||||
-------
|
||||
A new :class:`_InteractFactory` which will apply the
|
||||
options when called.
|
||||
"""
|
||||
opts = dict(self.opts)
|
||||
for k in kwds:
|
||||
try:
|
||||
# Ensure that the key exists because we want to change
|
||||
# existing options, not add new ones.
|
||||
_ = opts[k]
|
||||
except KeyError:
|
||||
raise ValueError("invalid option {!r}".format(k))
|
||||
opts[k] = kwds[k]
|
||||
return type(self)(self.cls, opts, self.kwargs)
|
||||
|
||||
|
||||
interact = interactive.factory()
|
||||
interact_manual = interact.options(manual=True, manual_name="Run Interact")
|
||||
|
||||
|
||||
class fixed(HasTraits):
|
||||
"""A pseudo-widget whose value is fixed and never synced to the client."""
|
||||
value = Any(help="Any Python object")
|
||||
description = Unicode('', help="Any Python object")
|
||||
def __init__(self, value, **kwargs):
|
||||
super(fixed, self).__init__(value=value, **kwargs)
|
||||
def get_interact_value(self):
|
||||
"""Return the value for this widget which should be passed to
|
||||
interactive functions. Custom widgets can change this method
|
||||
to process the raw value ``self.value``.
|
||||
"""
|
||||
return self.value
|
||||
Loading…
Add table
Add a link
Reference in a new issue