Uploaded Test files
This commit is contained in:
parent
f584ad9d97
commit
2e81cb7d99
16627 changed files with 2065359 additions and 102444 deletions
29
venv/Lib/site-packages/ipywidgets/widgets/__init__.py
Normal file
29
venv/Lib/site-packages/ipywidgets/widgets/__init__.py
Normal file
|
@ -0,0 +1,29 @@
|
|||
# Copyright (c) Jupyter Development Team.
|
||||
# Distributed under the terms of the Modified BSD License.
|
||||
|
||||
from .widget import Widget, CallbackDispatcher, register, widget_serialization
|
||||
from .domwidget import DOMWidget
|
||||
from .valuewidget import ValueWidget
|
||||
|
||||
from .trait_types import Color, Datetime, NumberFormat
|
||||
|
||||
from .widget_core import CoreWidget
|
||||
from .widget_bool import Checkbox, ToggleButton, Valid
|
||||
from .widget_button import Button, ButtonStyle
|
||||
from .widget_box import Box, HBox, VBox, GridBox
|
||||
from .widget_float import FloatText, BoundedFloatText, FloatSlider, FloatProgress, FloatRangeSlider, FloatLogSlider
|
||||
from .widget_int import IntText, BoundedIntText, IntSlider, IntProgress, IntRangeSlider, Play, SliderStyle
|
||||
from .widget_color import ColorPicker
|
||||
from .widget_date import DatePicker
|
||||
from .widget_output import Output
|
||||
from .widget_selection import RadioButtons, ToggleButtons, ToggleButtonsStyle, Dropdown, Select, SelectionSlider, SelectMultiple, SelectionRangeSlider
|
||||
from .widget_selectioncontainer import Tab, Accordion
|
||||
from .widget_string import HTML, HTMLMath, Label, Text, Textarea, Password, Combobox
|
||||
from .widget_controller import Controller
|
||||
from .interaction import interact, interactive, fixed, interact_manual, interactive_output
|
||||
from .widget_link import jslink, jsdlink
|
||||
from .widget_layout import Layout
|
||||
from .widget_media import Image, Video, Audio
|
||||
from .widget_style import Style
|
||||
from .widget_templates import TwoByTwoLayout, AppLayout, GridspecLayout
|
||||
from .widget_upload import FileUpload
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
13
venv/Lib/site-packages/ipywidgets/widgets/docutils.py
Normal file
13
venv/Lib/site-packages/ipywidgets/widgets/docutils.py
Normal file
|
@ -0,0 +1,13 @@
|
|||
# Copyright (c) Jupyter Development Team.
|
||||
# Distributed under the terms of the Modified BSD License.
|
||||
|
||||
def doc_subst(snippets):
|
||||
""" Substitute format strings in class or function docstring """
|
||||
def decorator(cls):
|
||||
# Strip the snippets to avoid trailing new lines and whitespace
|
||||
stripped_snippets = {
|
||||
key: snippet.strip() for (key, snippet) in snippets.items()
|
||||
}
|
||||
cls.__doc__ = cls.__doc__.format(**stripped_snippets)
|
||||
return cls
|
||||
return decorator
|
50
venv/Lib/site-packages/ipywidgets/widgets/domwidget.py
Normal file
50
venv/Lib/site-packages/ipywidgets/widgets/domwidget.py
Normal file
|
@ -0,0 +1,50 @@
|
|||
# Copyright (c) Jupyter Development Team.
|
||||
# Distributed under the terms of the Modified BSD License.
|
||||
|
||||
"""Contains the DOMWidget class"""
|
||||
|
||||
from traitlets import Unicode
|
||||
from .widget import Widget, widget_serialization
|
||||
from .trait_types import InstanceDict, TypedTuple
|
||||
from .widget_layout import Layout
|
||||
from .widget_style import Style
|
||||
|
||||
|
||||
class DOMWidget(Widget):
|
||||
"""Widget that can be inserted into the DOM"""
|
||||
|
||||
_model_name = Unicode('DOMWidgetModel').tag(sync=True)
|
||||
_dom_classes = TypedTuple(trait=Unicode(), help="CSS classes applied to widget DOM element").tag(sync=True)
|
||||
layout = InstanceDict(Layout).tag(sync=True, **widget_serialization)
|
||||
|
||||
def add_class(self, className):
|
||||
"""
|
||||
Adds a class to the top level element of the widget.
|
||||
|
||||
Doesn't add the class if it already exists.
|
||||
"""
|
||||
if className not in self._dom_classes:
|
||||
self._dom_classes = list(self._dom_classes) + [className]
|
||||
return self
|
||||
|
||||
def remove_class(self, className):
|
||||
"""
|
||||
Removes a class from the top level element of the widget.
|
||||
|
||||
Doesn't remove the class if it doesn't exist.
|
||||
"""
|
||||
if className in self._dom_classes:
|
||||
self._dom_classes = [c for c in self._dom_classes if c != className]
|
||||
return self
|
||||
|
||||
def _repr_keys(self):
|
||||
for key in super(DOMWidget, self)._repr_keys():
|
||||
# Exclude layout if it had the default value
|
||||
if key == 'layout':
|
||||
value = getattr(self, key)
|
||||
if repr(value) == '%s()' % value.__class__.__name__:
|
||||
continue
|
||||
yield key
|
||||
# We also need to include _dom_classes in repr for reproducibility
|
||||
if self._dom_classes:
|
||||
yield '_dom_classes'
|
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
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
After Width: | Height: | Size: 35 KiB |
|
@ -0,0 +1,27 @@
|
|||
# Copyright (c) Jupyter Development Team.
|
||||
# Distributed under the terms of the Modified BSD License.
|
||||
|
||||
from unittest import TestCase
|
||||
|
||||
from ipywidgets.widgets.docutils import doc_subst
|
||||
|
||||
|
||||
class TestDocSubst(TestCase):
|
||||
|
||||
def test_substitution(self):
|
||||
snippets = {'key': '62'}
|
||||
|
||||
@doc_subst(snippets)
|
||||
def f():
|
||||
""" Docstring with value {key} """
|
||||
|
||||
assert f.__doc__ == " Docstring with value 62 "
|
||||
|
||||
def test_unused_keys(self):
|
||||
snippets = {'key': '62', 'other-key': 'unused'}
|
||||
|
||||
@doc_subst(snippets)
|
||||
def f():
|
||||
""" Docstring with value {key} """
|
||||
|
||||
assert f.__doc__ == " Docstring with value 62 "
|
|
@ -0,0 +1,732 @@
|
|||
# Copyright (c) Jupyter Development Team.
|
||||
# Distributed under the terms of the Modified BSD License.
|
||||
|
||||
"""Test interact and interactive."""
|
||||
|
||||
from __future__ import print_function
|
||||
|
||||
try:
|
||||
from unittest.mock import patch
|
||||
except ImportError:
|
||||
from mock import patch
|
||||
|
||||
import os
|
||||
from collections import OrderedDict
|
||||
import pytest
|
||||
|
||||
import ipywidgets as widgets
|
||||
|
||||
from traitlets import TraitError
|
||||
from ipywidgets import (interact, interact_manual, interactive,
|
||||
interaction, Output)
|
||||
from ipython_genutils.py3compat import annotate
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
# Utility stuff
|
||||
#-----------------------------------------------------------------------------
|
||||
|
||||
from .utils import setup, teardown
|
||||
|
||||
def f(**kwargs):
|
||||
pass
|
||||
|
||||
displayed = []
|
||||
@pytest.fixture()
|
||||
def clear_display():
|
||||
global displayed
|
||||
displayed = []
|
||||
|
||||
def record_display(*args):
|
||||
displayed.extend(args)
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
# Actual tests
|
||||
#-----------------------------------------------------------------------------
|
||||
|
||||
def check_widget(w, **d):
|
||||
"""Check a single widget against a dict"""
|
||||
for attr, expected in d.items():
|
||||
if attr == 'cls':
|
||||
assert w.__class__ is expected
|
||||
else:
|
||||
value = getattr(w, attr)
|
||||
assert value == expected, "%s.%s = %r != %r" % (w.__class__.__name__, attr, value, expected)
|
||||
|
||||
# For numeric values, the types should match too
|
||||
if isinstance(value, (int, float)):
|
||||
tv = type(value)
|
||||
te = type(expected)
|
||||
assert tv is te, "type(%s.%s) = %r != %r" % (w.__class__.__name__, attr, tv, te)
|
||||
|
||||
def check_widgets(container, **to_check):
|
||||
"""Check that widgets are created as expected"""
|
||||
# build a widget dictionary, so it matches
|
||||
widgets = {}
|
||||
for w in container.children:
|
||||
if not isinstance(w, Output):
|
||||
widgets[w.description] = w
|
||||
|
||||
for key, d in to_check.items():
|
||||
assert key in widgets
|
||||
check_widget(widgets[key], **d)
|
||||
|
||||
|
||||
def test_single_value_string():
|
||||
a = u'hello'
|
||||
c = interactive(f, a=a)
|
||||
w = c.children[0]
|
||||
check_widget(w,
|
||||
cls=widgets.Text,
|
||||
description='a',
|
||||
value=a,
|
||||
)
|
||||
|
||||
def test_single_value_bool():
|
||||
for a in (True, False):
|
||||
c = interactive(f, a=a)
|
||||
w = c.children[0]
|
||||
check_widget(w,
|
||||
cls=widgets.Checkbox,
|
||||
description='a',
|
||||
value=a,
|
||||
)
|
||||
|
||||
def test_single_value_float():
|
||||
for a in (2.25, 1.0, -3.5, 0.0):
|
||||
if not a:
|
||||
expected_min = 0.0
|
||||
expected_max = 1.0
|
||||
elif a > 0:
|
||||
expected_min = -a
|
||||
expected_max = 3*a
|
||||
else:
|
||||
expected_min = 3*a
|
||||
expected_max = -a
|
||||
c = interactive(f, a=a)
|
||||
w = c.children[0]
|
||||
check_widget(w,
|
||||
cls=widgets.FloatSlider,
|
||||
description='a',
|
||||
value=a,
|
||||
min=expected_min,
|
||||
max=expected_max,
|
||||
step=0.1,
|
||||
readout=True,
|
||||
)
|
||||
|
||||
def test_single_value_int():
|
||||
for a in (1, 5, -3, 0):
|
||||
if not a:
|
||||
expected_min = 0
|
||||
expected_max = 1
|
||||
elif a > 0:
|
||||
expected_min = -a
|
||||
expected_max = 3*a
|
||||
else:
|
||||
expected_min = 3*a
|
||||
expected_max = -a
|
||||
c = interactive(f, a=a)
|
||||
assert len(c.children) == 2
|
||||
w = c.children[0]
|
||||
check_widget(w,
|
||||
cls=widgets.IntSlider,
|
||||
description='a',
|
||||
value=a,
|
||||
min=expected_min,
|
||||
max=expected_max,
|
||||
step=1,
|
||||
readout=True,
|
||||
)
|
||||
|
||||
def test_list_str():
|
||||
values = ['hello', 'there', 'guy']
|
||||
first = values[0]
|
||||
c = interactive(f, lis=values)
|
||||
assert len(c.children) == 2
|
||||
d = dict(
|
||||
cls=widgets.Dropdown,
|
||||
value=first,
|
||||
options=tuple(values),
|
||||
_options_labels=tuple(values),
|
||||
_options_values=tuple(values),
|
||||
)
|
||||
check_widgets(c, lis=d)
|
||||
|
||||
def test_list_int():
|
||||
values = [3, 1, 2]
|
||||
first = values[0]
|
||||
c = interactive(f, lis=values)
|
||||
assert len(c.children) == 2
|
||||
d = dict(
|
||||
cls=widgets.Dropdown,
|
||||
value=first,
|
||||
options=tuple(values),
|
||||
_options_labels=tuple(str(v) for v in values),
|
||||
_options_values=tuple(values),
|
||||
)
|
||||
check_widgets(c, lis=d)
|
||||
|
||||
def test_list_tuple():
|
||||
values = [(3, 300), (1, 100), (2, 200)]
|
||||
first = values[0][1]
|
||||
c = interactive(f, lis=values)
|
||||
assert len(c.children) == 2
|
||||
d = dict(
|
||||
cls=widgets.Dropdown,
|
||||
value=first,
|
||||
options=tuple(values),
|
||||
_options_labels=("3", "1", "2"),
|
||||
_options_values=(300, 100, 200),
|
||||
)
|
||||
check_widgets(c, lis=d)
|
||||
|
||||
def test_list_tuple_invalid():
|
||||
for bad in [
|
||||
(),
|
||||
]:
|
||||
with pytest.raises(ValueError):
|
||||
print(bad) # because there is no custom message in assert_raises
|
||||
c = interactive(f, tup=bad)
|
||||
|
||||
def test_dict():
|
||||
for d in [
|
||||
dict(a=5),
|
||||
dict(a=5, b='b', c=dict),
|
||||
]:
|
||||
c = interactive(f, d=d)
|
||||
w = c.children[0]
|
||||
check = dict(
|
||||
cls=widgets.Dropdown,
|
||||
description='d',
|
||||
value=next(iter(d.values())),
|
||||
options=d,
|
||||
_options_labels=tuple(d.keys()),
|
||||
_options_values=tuple(d.values()),
|
||||
)
|
||||
check_widget(w, **check)
|
||||
|
||||
|
||||
def test_ordereddict():
|
||||
from collections import OrderedDict
|
||||
items = [(3, 300), (1, 100), (2, 200)]
|
||||
first = items[0][1]
|
||||
values = OrderedDict(items)
|
||||
c = interactive(f, lis=values)
|
||||
assert len(c.children) == 2
|
||||
d = dict(
|
||||
cls=widgets.Dropdown,
|
||||
value=first,
|
||||
options=values,
|
||||
_options_labels=("3", "1", "2"),
|
||||
_options_values=(300, 100, 200),
|
||||
)
|
||||
check_widgets(c, lis=d)
|
||||
|
||||
def test_iterable():
|
||||
def yield_values():
|
||||
yield 3
|
||||
yield 1
|
||||
yield 2
|
||||
first = next(yield_values())
|
||||
c = interactive(f, lis=yield_values())
|
||||
assert len(c.children) == 2
|
||||
d = dict(
|
||||
cls=widgets.Dropdown,
|
||||
value=first,
|
||||
options=(3, 1, 2),
|
||||
_options_labels=("3", "1", "2"),
|
||||
_options_values=(3, 1, 2),
|
||||
)
|
||||
check_widgets(c, lis=d)
|
||||
|
||||
def test_iterable_tuple():
|
||||
values = [(3, 300), (1, 100), (2, 200)]
|
||||
first = values[0][1]
|
||||
c = interactive(f, lis=iter(values))
|
||||
assert len(c.children) == 2
|
||||
d = dict(
|
||||
cls=widgets.Dropdown,
|
||||
value=first,
|
||||
options=tuple(values),
|
||||
_options_labels=("3", "1", "2"),
|
||||
_options_values=(300, 100, 200),
|
||||
)
|
||||
check_widgets(c, lis=d)
|
||||
|
||||
def test_mapping():
|
||||
from collections import Mapping, OrderedDict
|
||||
class TestMapping(Mapping):
|
||||
def __init__(self, values):
|
||||
self.values = values
|
||||
def __getitem__(self):
|
||||
raise NotImplementedError
|
||||
def __len__(self):
|
||||
raise NotImplementedError
|
||||
def __iter__(self):
|
||||
raise NotImplementedError
|
||||
def items(self):
|
||||
return self.values
|
||||
|
||||
items = [(3, 300), (1, 100), (2, 200)]
|
||||
first = items[0][1]
|
||||
values = TestMapping(items)
|
||||
c = interactive(f, lis=values)
|
||||
assert len(c.children) == 2
|
||||
d = dict(
|
||||
cls=widgets.Dropdown,
|
||||
value=first,
|
||||
options=tuple(items),
|
||||
_options_labels=("3", "1", "2"),
|
||||
_options_values=(300, 100, 200),
|
||||
)
|
||||
check_widgets(c, lis=d)
|
||||
|
||||
|
||||
def test_defaults():
|
||||
@annotate(n=10)
|
||||
def f(n, f=4.5, g=1):
|
||||
pass
|
||||
|
||||
c = interactive(f)
|
||||
check_widgets(c,
|
||||
n=dict(
|
||||
cls=widgets.IntSlider,
|
||||
value=10,
|
||||
),
|
||||
f=dict(
|
||||
cls=widgets.FloatSlider,
|
||||
value=4.5,
|
||||
),
|
||||
g=dict(
|
||||
cls=widgets.IntSlider,
|
||||
value=1,
|
||||
),
|
||||
)
|
||||
|
||||
def test_default_values():
|
||||
@annotate(n=10, f=(0, 10.), g=5, h=OrderedDict([('a',1), ('b',2)]), j=['hi', 'there'])
|
||||
def f(n, f=4.5, g=1, h=2, j='there'):
|
||||
pass
|
||||
|
||||
c = interactive(f)
|
||||
check_widgets(c,
|
||||
n=dict(
|
||||
cls=widgets.IntSlider,
|
||||
value=10,
|
||||
),
|
||||
f=dict(
|
||||
cls=widgets.FloatSlider,
|
||||
value=4.5,
|
||||
),
|
||||
g=dict(
|
||||
cls=widgets.IntSlider,
|
||||
value=5,
|
||||
),
|
||||
h=dict(
|
||||
cls=widgets.Dropdown,
|
||||
options=OrderedDict([('a',1), ('b',2)]),
|
||||
value=2
|
||||
),
|
||||
j=dict(
|
||||
cls=widgets.Dropdown,
|
||||
options=('hi', 'there'),
|
||||
value='there'
|
||||
),
|
||||
)
|
||||
|
||||
def test_default_out_of_bounds():
|
||||
@annotate(f=(0, 10.), h={'a': 1}, j=['hi', 'there'])
|
||||
def f(f='hi', h=5, j='other'):
|
||||
pass
|
||||
|
||||
c = interactive(f)
|
||||
check_widgets(c,
|
||||
f=dict(
|
||||
cls=widgets.FloatSlider,
|
||||
value=5.,
|
||||
),
|
||||
h=dict(
|
||||
cls=widgets.Dropdown,
|
||||
options={'a': 1},
|
||||
value=1,
|
||||
),
|
||||
j=dict(
|
||||
cls=widgets.Dropdown,
|
||||
options=('hi', 'there'),
|
||||
value='hi',
|
||||
),
|
||||
)
|
||||
|
||||
def test_annotations():
|
||||
@annotate(n=10, f=widgets.FloatText())
|
||||
def f(n, f):
|
||||
pass
|
||||
|
||||
c = interactive(f)
|
||||
check_widgets(c,
|
||||
n=dict(
|
||||
cls=widgets.IntSlider,
|
||||
value=10,
|
||||
),
|
||||
f=dict(
|
||||
cls=widgets.FloatText,
|
||||
),
|
||||
)
|
||||
|
||||
def test_priority():
|
||||
@annotate(annotate='annotate', kwarg='annotate')
|
||||
def f(kwarg='default', annotate='default', default='default'):
|
||||
pass
|
||||
|
||||
c = interactive(f, kwarg='kwarg')
|
||||
check_widgets(c,
|
||||
kwarg=dict(
|
||||
cls=widgets.Text,
|
||||
value='kwarg',
|
||||
),
|
||||
annotate=dict(
|
||||
cls=widgets.Text,
|
||||
value='annotate',
|
||||
),
|
||||
)
|
||||
|
||||
def test_decorator_kwarg(clear_display):
|
||||
with patch.object(interaction, 'display', record_display):
|
||||
@interact(a=5)
|
||||
def foo(a):
|
||||
pass
|
||||
assert len(displayed) == 1
|
||||
w = displayed[0].children[0]
|
||||
check_widget(w,
|
||||
cls=widgets.IntSlider,
|
||||
value=5,
|
||||
)
|
||||
|
||||
def test_interact_instancemethod(clear_display):
|
||||
class Foo(object):
|
||||
def show(self, x):
|
||||
print(x)
|
||||
|
||||
f = Foo()
|
||||
|
||||
with patch.object(interaction, 'display', record_display):
|
||||
g = interact(f.show, x=(1,10))
|
||||
assert len(displayed) == 1
|
||||
w = displayed[0].children[0]
|
||||
check_widget(w,
|
||||
cls=widgets.IntSlider,
|
||||
value=5,
|
||||
)
|
||||
|
||||
def test_decorator_no_call(clear_display):
|
||||
with patch.object(interaction, 'display', record_display):
|
||||
@interact
|
||||
def foo(a='default'):
|
||||
pass
|
||||
assert len(displayed) == 1
|
||||
w = displayed[0].children[0]
|
||||
check_widget(w,
|
||||
cls=widgets.Text,
|
||||
value='default',
|
||||
)
|
||||
|
||||
def test_call_interact(clear_display):
|
||||
def foo(a='default'):
|
||||
pass
|
||||
with patch.object(interaction, 'display', record_display):
|
||||
ifoo = interact(foo)
|
||||
assert len(displayed) == 1
|
||||
w = displayed[0].children[0]
|
||||
check_widget(w,
|
||||
cls=widgets.Text,
|
||||
value='default',
|
||||
)
|
||||
|
||||
def test_call_interact_on_trait_changed_none_return(clear_display):
|
||||
def foo(a='default'):
|
||||
pass
|
||||
with patch.object(interaction, 'display', record_display):
|
||||
ifoo = interact(foo)
|
||||
assert len(displayed) == 1
|
||||
w = displayed[0].children[0]
|
||||
check_widget(w,
|
||||
cls=widgets.Text,
|
||||
value='default',
|
||||
)
|
||||
with patch.object(interaction, 'display', record_display):
|
||||
w.value = 'called'
|
||||
assert len(displayed) == 1
|
||||
|
||||
def test_call_interact_kwargs(clear_display):
|
||||
def foo(a='default'):
|
||||
pass
|
||||
with patch.object(interaction, 'display', record_display):
|
||||
ifoo = interact(foo, a=10)
|
||||
assert len(displayed) == 1
|
||||
w = displayed[0].children[0]
|
||||
check_widget(w,
|
||||
cls=widgets.IntSlider,
|
||||
value=10,
|
||||
)
|
||||
|
||||
def test_call_decorated_on_trait_change(clear_display):
|
||||
"""test calling @interact decorated functions"""
|
||||
d = {}
|
||||
with patch.object(interaction, 'display', record_display):
|
||||
@interact
|
||||
def foo(a='default'):
|
||||
d['a'] = a
|
||||
return a
|
||||
assert len(displayed) == 1
|
||||
w = displayed[0].children[0]
|
||||
check_widget(w,
|
||||
cls=widgets.Text,
|
||||
value='default',
|
||||
)
|
||||
# test calling the function directly
|
||||
a = foo('hello')
|
||||
assert a == 'hello'
|
||||
assert d['a'] == 'hello'
|
||||
|
||||
# test that setting trait values calls the function
|
||||
with patch.object(interaction, 'display', record_display):
|
||||
w.value = 'called'
|
||||
assert d['a'] == 'called'
|
||||
assert len(displayed) == 2
|
||||
assert w.value == displayed[-1]
|
||||
|
||||
def test_call_decorated_kwargs_on_trait_change(clear_display):
|
||||
"""test calling @interact(foo=bar) decorated functions"""
|
||||
d = {}
|
||||
with patch.object(interaction, 'display', record_display):
|
||||
@interact(a='kwarg')
|
||||
def foo(a='default'):
|
||||
d['a'] = a
|
||||
return a
|
||||
assert len(displayed) == 1
|
||||
w = displayed[0].children[0]
|
||||
check_widget(w,
|
||||
cls=widgets.Text,
|
||||
value='kwarg',
|
||||
)
|
||||
# test calling the function directly
|
||||
a = foo('hello')
|
||||
assert a == 'hello'
|
||||
assert d['a'] == 'hello'
|
||||
|
||||
# test that setting trait values calls the function
|
||||
with patch.object(interaction, 'display', record_display):
|
||||
w.value = 'called'
|
||||
assert d['a'] == 'called'
|
||||
assert len(displayed) == 2
|
||||
assert w.value == displayed[-1]
|
||||
|
||||
|
||||
|
||||
def test_fixed():
|
||||
c = interactive(f, a=widgets.fixed(5), b='text')
|
||||
assert len(c.children) == 2
|
||||
w = c.children[0]
|
||||
check_widget(w,
|
||||
cls=widgets.Text,
|
||||
value='text',
|
||||
description='b',
|
||||
)
|
||||
|
||||
def test_default_description():
|
||||
c = interactive(f, b='text')
|
||||
w = c.children[0]
|
||||
check_widget(w,
|
||||
cls=widgets.Text,
|
||||
value='text',
|
||||
description='b',
|
||||
)
|
||||
|
||||
def test_custom_description():
|
||||
d = {}
|
||||
def record_kwargs(**kwargs):
|
||||
d.clear()
|
||||
d.update(kwargs)
|
||||
|
||||
c = interactive(record_kwargs, b=widgets.Text(value='text', description='foo'))
|
||||
w = c.children[0]
|
||||
check_widget(w,
|
||||
cls=widgets.Text,
|
||||
value='text',
|
||||
description='foo',
|
||||
)
|
||||
w.value = 'different text'
|
||||
assert d == {'b': 'different text'}
|
||||
|
||||
def test_interact_manual_button():
|
||||
c = interact.options(manual=True).widget(f)
|
||||
w = c.children[0]
|
||||
check_widget(w, cls=widgets.Button)
|
||||
|
||||
def test_interact_manual_nocall():
|
||||
callcount = 0
|
||||
def calltest(testarg):
|
||||
callcount += 1
|
||||
c = interact.options(manual=True)(calltest, testarg=5).widget
|
||||
c.children[0].value = 10
|
||||
assert callcount == 0
|
||||
|
||||
def test_interact_call():
|
||||
w = interact.widget(f)
|
||||
w.update()
|
||||
|
||||
w = interact_manual.widget(f)
|
||||
w.update()
|
||||
|
||||
def test_interact_options():
|
||||
def f(x):
|
||||
return x
|
||||
w = interact.options(manual=False).options(manual=True)(f, x=21).widget
|
||||
assert w.manual == True
|
||||
|
||||
w = interact_manual.options(manual=False).options()(x=21).widget(f)
|
||||
assert w.manual == False
|
||||
|
||||
w = interact(x=21)().options(manual=True)(f).widget
|
||||
assert w.manual == True
|
||||
|
||||
def test_interact_options_bad():
|
||||
with pytest.raises(ValueError):
|
||||
interact.options(bad="foo")
|
||||
|
||||
def test_int_range_logic():
|
||||
irsw = widgets.IntRangeSlider
|
||||
w = irsw(value=(2, 4), min=0, max=6)
|
||||
check_widget(w, cls=irsw, value=(2, 4), min=0, max=6)
|
||||
w.upper = 3
|
||||
w.max = 3
|
||||
check_widget(w, cls=irsw, value=(2, 3), min=0, max=3)
|
||||
|
||||
w.min = 0
|
||||
w.max = 6
|
||||
w.lower = 2
|
||||
w.upper = 4
|
||||
check_widget(w, cls=irsw, value=(2, 4), min=0, max=6)
|
||||
w.value = (0, 1) #lower non-overlapping range
|
||||
check_widget(w, cls=irsw, value=(0, 1), min=0, max=6)
|
||||
w.value = (5, 6) #upper non-overlapping range
|
||||
check_widget(w, cls=irsw, value=(5, 6), min=0, max=6)
|
||||
w.lower = 2
|
||||
check_widget(w, cls=irsw, value=(2, 6), min=0, max=6)
|
||||
|
||||
with pytest.raises(TraitError):
|
||||
w.min = 7
|
||||
with pytest.raises(TraitError):
|
||||
w.max = -1
|
||||
|
||||
w = irsw(min=2, max=3, value=(2, 3))
|
||||
check_widget(w, min=2, max=3, value=(2, 3))
|
||||
w = irsw(min=100, max=200, value=(125, 175))
|
||||
check_widget(w, value=(125, 175))
|
||||
|
||||
with pytest.raises(TraitError):
|
||||
irsw(min=2, max=1)
|
||||
|
||||
|
||||
def test_float_range_logic():
|
||||
frsw = widgets.FloatRangeSlider
|
||||
w = frsw(value=(.2, .4), min=0., max=.6)
|
||||
check_widget(w, cls=frsw, value=(.2, .4), min=0., max=.6)
|
||||
|
||||
w.min = 0.
|
||||
w.max = .6
|
||||
w.lower = .2
|
||||
w.upper = .4
|
||||
check_widget(w, cls=frsw, value=(.2, .4), min=0., max=.6)
|
||||
w.value = (0., .1) #lower non-overlapping range
|
||||
check_widget(w, cls=frsw, value=(0., .1), min=0., max=.6)
|
||||
w.value = (.5, .6) #upper non-overlapping range
|
||||
check_widget(w, cls=frsw, value=(.5, .6), min=0., max=.6)
|
||||
w.lower = .2
|
||||
check_widget(w, cls=frsw, value=(.2, .6), min=0., max=.6)
|
||||
|
||||
with pytest.raises(TraitError):
|
||||
w.min = .7
|
||||
with pytest.raises(TraitError):
|
||||
w.max = -.1
|
||||
|
||||
w = frsw(min=2, max=3, value=(2.2, 2.5))
|
||||
check_widget(w, min=2., max=3.)
|
||||
|
||||
with pytest.raises(TraitError):
|
||||
frsw(min=.2, max=.1)
|
||||
|
||||
|
||||
def test_multiple_selection():
|
||||
smw = widgets.SelectMultiple
|
||||
|
||||
# degenerate multiple select
|
||||
w = smw()
|
||||
check_widget(w, value=tuple())
|
||||
|
||||
# don't accept random other value when no options
|
||||
with pytest.raises(TraitError):
|
||||
w.value = (2,)
|
||||
check_widget(w, value=tuple())
|
||||
|
||||
# basic multiple select
|
||||
w = smw(options=[(1, 1)], value=[1])
|
||||
check_widget(w, cls=smw, value=(1,), options=((1, 1),))
|
||||
|
||||
# don't accept random other value
|
||||
with pytest.raises(TraitError):
|
||||
w.value = w.value + (2,)
|
||||
check_widget(w, value=(1,))
|
||||
|
||||
# change options, which resets value
|
||||
w.options = w.options + ((2, 2),)
|
||||
check_widget(w, options=((1, 1), (2,2)), value=())
|
||||
|
||||
# change value
|
||||
w.value = (1,2)
|
||||
check_widget(w, value=(1, 2))
|
||||
|
||||
# dict style
|
||||
w.options = {1: 1}
|
||||
check_widget(w, options={1:1})
|
||||
|
||||
# updating
|
||||
with pytest.raises(TraitError):
|
||||
w.value = (2,)
|
||||
check_widget(w, options={1:1})
|
||||
|
||||
|
||||
def test_interact_noinspect():
|
||||
a = u'hello'
|
||||
c = interactive(print, a=a)
|
||||
w = c.children[0]
|
||||
check_widget(w,
|
||||
cls=widgets.Text,
|
||||
description='a',
|
||||
value=a,
|
||||
)
|
||||
|
||||
|
||||
def test_get_interact_value():
|
||||
from ipywidgets.widgets import ValueWidget
|
||||
from traitlets import Unicode
|
||||
class TheAnswer(ValueWidget):
|
||||
_model_name = Unicode('TheAnswer')
|
||||
description = Unicode()
|
||||
def get_interact_value(self):
|
||||
return 42
|
||||
w = TheAnswer()
|
||||
c = interactive(lambda v: v, v=w)
|
||||
c.update()
|
||||
assert c.result == 42
|
||||
|
||||
def test_state_schema():
|
||||
from ipywidgets.widgets import IntSlider, Widget
|
||||
import json
|
||||
import jsonschema
|
||||
s = IntSlider()
|
||||
state = Widget.get_manager_state(drop_defaults=True)
|
||||
with open(os.path.join(os.path.dirname(os.path.realpath(__file__)), '../../', 'state.schema.json')) as f:
|
||||
schema = json.load(f)
|
||||
jsonschema.validate(state, schema)
|
||||
|
39
venv/Lib/site-packages/ipywidgets/widgets/tests/test_link.py
Normal file
39
venv/Lib/site-packages/ipywidgets/widgets/tests/test_link.py
Normal file
|
@ -0,0 +1,39 @@
|
|||
# Copyright (c) Jupyter Development Team.
|
||||
# Distributed under the terms of the Modified BSD License.
|
||||
|
||||
import pytest
|
||||
|
||||
from .. import jslink, jsdlink, ToggleButton
|
||||
from .utils import setup, teardown
|
||||
|
||||
def test_jslink_args():
|
||||
with pytest.raises(TypeError):
|
||||
jslink()
|
||||
w1 = ToggleButton()
|
||||
with pytest.raises(TypeError):
|
||||
jslink((w1, 'value'))
|
||||
|
||||
w2 = ToggleButton()
|
||||
jslink((w1, 'value'), (w2, 'value'))
|
||||
|
||||
with pytest.raises(TypeError):
|
||||
jslink((w1, 'value'), (w2, 'nosuchtrait'))
|
||||
|
||||
with pytest.raises(TypeError):
|
||||
jslink((w1, 'value'), (w2, 'traits'))
|
||||
|
||||
def test_jsdlink_args():
|
||||
with pytest.raises(TypeError):
|
||||
jsdlink()
|
||||
w1 = ToggleButton()
|
||||
with pytest.raises(TypeError):
|
||||
jsdlink((w1, 'value'))
|
||||
|
||||
w2 = ToggleButton()
|
||||
jsdlink((w1, 'value'), (w2, 'value'))
|
||||
|
||||
with pytest.raises(TypeError):
|
||||
jsdlink((w1, 'value'), (w2, 'nosuchtrait'))
|
||||
|
||||
with pytest.raises(TypeError):
|
||||
jsdlink((w1, 'value'), (w2, 'traits'))
|
|
@ -0,0 +1,28 @@
|
|||
# Copyright (c) Jupyter Development Team.
|
||||
# Distributed under the terms of the Modified BSD License.
|
||||
|
||||
from unittest import TestCase
|
||||
|
||||
from traitlets import TraitError
|
||||
|
||||
from ipywidgets.widgets import Accordion, HTML
|
||||
|
||||
|
||||
class TestAccordion(TestCase):
|
||||
|
||||
def setUp(self):
|
||||
self.children = [HTML('0'), HTML('1')]
|
||||
|
||||
def test_selected_index_none(self):
|
||||
accordion = Accordion(self.children, selected_index=None)
|
||||
state = accordion.get_state()
|
||||
assert state['selected_index'] is None
|
||||
|
||||
def test_selected_index(self):
|
||||
accordion = Accordion(self.children, selected_index=1)
|
||||
state = accordion.get_state()
|
||||
assert state['selected_index'] == 1
|
||||
|
||||
def test_selected_index_out_of_bounds(self):
|
||||
with self.assertRaises(TraitError):
|
||||
Accordion(self.children, selected_index=-1)
|
|
@ -0,0 +1,25 @@
|
|||
# Copyright (c) Jupyter Development Team.
|
||||
# Distributed under the terms of the Modified BSD License.
|
||||
|
||||
from traitlets import Bool, Tuple, List
|
||||
|
||||
from .utils import setup, teardown
|
||||
|
||||
from ..widget import Widget
|
||||
|
||||
# A widget with simple traits
|
||||
class SimpleWidget(Widget):
|
||||
a = Bool().tag(sync=True)
|
||||
b = Tuple(Bool(), Bool(), Bool(), default_value=(False, False, False)).tag(sync=True)
|
||||
c = List(Bool()).tag(sync=True)
|
||||
|
||||
def test_empty_send_state():
|
||||
w = SimpleWidget()
|
||||
w.send_state([])
|
||||
assert w.comm.messages == []
|
||||
|
||||
def test_empty_hold_sync():
|
||||
w = SimpleWidget()
|
||||
with w.hold_sync():
|
||||
pass
|
||||
assert w.comm.messages == []
|
|
@ -0,0 +1,253 @@
|
|||
# Copyright (c) Jupyter Development Team.
|
||||
# Distributed under the terms of the Modified BSD License.
|
||||
|
||||
from ipython_genutils.py3compat import PY3
|
||||
|
||||
import pytest
|
||||
try:
|
||||
from unittest import mock
|
||||
except ImportError:
|
||||
import mock
|
||||
|
||||
|
||||
from traitlets import Bool, Tuple, List, Instance, CFloat, CInt, Float, Int, TraitError, observe
|
||||
|
||||
from .utils import setup, teardown
|
||||
|
||||
from ..widget import Widget
|
||||
|
||||
#
|
||||
# First some widgets to test on:
|
||||
#
|
||||
|
||||
# A widget with simple traits (list + tuple to ensure both are handled)
|
||||
class SimpleWidget(Widget):
|
||||
a = Bool().tag(sync=True)
|
||||
b = Tuple(Bool(), Bool(), Bool(), default_value=(False, False, False)).tag(sync=True)
|
||||
c = List(Bool()).tag(sync=True)
|
||||
|
||||
|
||||
# A widget with various kinds of number traits
|
||||
class NumberWidget(Widget):
|
||||
f = Float().tag(sync=True)
|
||||
cf = CFloat().tag(sync=True)
|
||||
i = Int().tag(sync=True)
|
||||
ci = CInt().tag(sync=True)
|
||||
|
||||
|
||||
|
||||
# A widget where the data might be changed on reception:
|
||||
def transform_fromjson(data, widget):
|
||||
# Switch the two last elements when setting from json, if the first element is True
|
||||
# and always set first element to False
|
||||
if not data[0]:
|
||||
return data
|
||||
return [False] + data[1:-2] + [data[-1], data[-2]]
|
||||
|
||||
class TransformerWidget(Widget):
|
||||
d = List(Bool()).tag(sync=True, from_json=transform_fromjson)
|
||||
|
||||
|
||||
|
||||
# A widget that has a buffer:
|
||||
class DataInstance():
|
||||
def __init__(self, data=None):
|
||||
self.data = data
|
||||
|
||||
def mview_serializer(instance, widget):
|
||||
return { 'data': memoryview(instance.data) if instance.data else None }
|
||||
|
||||
def bytes_serializer(instance, widget):
|
||||
return { 'data': bytearray(memoryview(instance.data).tobytes()) if instance.data else None }
|
||||
|
||||
def deserializer(json_data, widget):
|
||||
return DataInstance( memoryview(json_data['data']).tobytes() if json_data else None )
|
||||
|
||||
class DataWidget(SimpleWidget):
|
||||
d = Instance(DataInstance).tag(sync=True, to_json=mview_serializer, from_json=deserializer)
|
||||
|
||||
# A widget that has a buffer that might be changed on reception:
|
||||
def truncate_deserializer(json_data, widget):
|
||||
return DataInstance( json_data['data'][:20].tobytes() if json_data else None )
|
||||
|
||||
class TruncateDataWidget(SimpleWidget):
|
||||
d = Instance(DataInstance).tag(sync=True, to_json=bytes_serializer, from_json=truncate_deserializer)
|
||||
|
||||
|
||||
#
|
||||
# Actual tests:
|
||||
#
|
||||
|
||||
def test_set_state_simple():
|
||||
w = SimpleWidget()
|
||||
w.set_state(dict(
|
||||
a=True,
|
||||
b=[True, False, True],
|
||||
c=[False, True, False],
|
||||
))
|
||||
|
||||
assert w.comm.messages == []
|
||||
|
||||
|
||||
def test_set_state_transformer():
|
||||
w = TransformerWidget()
|
||||
w.set_state(dict(
|
||||
d=[True, False, True]
|
||||
))
|
||||
# Since the deserialize step changes the state, this should send an update
|
||||
assert w.comm.messages == [((), dict(
|
||||
buffers=[],
|
||||
data=dict(
|
||||
buffer_paths=[],
|
||||
method='update',
|
||||
state=dict(d=[False, True, False])
|
||||
)))]
|
||||
|
||||
|
||||
def test_set_state_data():
|
||||
w = DataWidget()
|
||||
data = memoryview(b'x'*30)
|
||||
w.set_state(dict(
|
||||
a=True,
|
||||
d={'data': data},
|
||||
))
|
||||
assert w.comm.messages == []
|
||||
|
||||
|
||||
def test_set_state_data_truncate():
|
||||
w = TruncateDataWidget()
|
||||
data = memoryview(b'x'*30)
|
||||
w.set_state(dict(
|
||||
a=True,
|
||||
d={'data': data},
|
||||
))
|
||||
# Get message for checking
|
||||
assert len(w.comm.messages) == 1 # ensure we didn't get more than expected
|
||||
msg = w.comm.messages[0]
|
||||
# Assert that the data update (truncation) sends an update
|
||||
buffers = msg[1].pop('buffers')
|
||||
assert msg == ((), dict(
|
||||
data=dict(
|
||||
buffer_paths=[['d', 'data']],
|
||||
method='update',
|
||||
state=dict(d={})
|
||||
)))
|
||||
|
||||
# Sanity:
|
||||
assert len(buffers) == 1
|
||||
assert buffers[0] == data[:20].tobytes()
|
||||
|
||||
|
||||
def test_set_state_numbers_int():
|
||||
# JS does not differentiate between float/int.
|
||||
# Instead, it formats exact floats as ints in JSON (1.0 -> '1').
|
||||
|
||||
w = NumberWidget()
|
||||
# Set everything with ints
|
||||
w.set_state(dict(
|
||||
f = 1,
|
||||
cf = 2,
|
||||
i = 3,
|
||||
ci = 4,
|
||||
))
|
||||
# Ensure no update message gets produced
|
||||
assert len(w.comm.messages) == 0
|
||||
|
||||
|
||||
def test_set_state_numbers_float():
|
||||
w = NumberWidget()
|
||||
# Set floats to int-like floats
|
||||
w.set_state(dict(
|
||||
f = 1.0,
|
||||
cf = 2.0,
|
||||
ci = 4.0
|
||||
))
|
||||
# Ensure no update message gets produced
|
||||
assert len(w.comm.messages) == 0
|
||||
|
||||
|
||||
def test_set_state_float_to_float():
|
||||
w = NumberWidget()
|
||||
# Set floats to float
|
||||
w.set_state(dict(
|
||||
f = 1.2,
|
||||
cf = 2.6,
|
||||
))
|
||||
# Ensure no update message gets produced
|
||||
assert len(w.comm.messages) == 0
|
||||
|
||||
|
||||
def test_set_state_cint_to_float():
|
||||
w = NumberWidget()
|
||||
|
||||
# Set CInt to float
|
||||
w.set_state(dict(
|
||||
ci = 5.6
|
||||
))
|
||||
# Ensure an update message gets produced
|
||||
assert len(w.comm.messages) == 1
|
||||
msg = w.comm.messages[0]
|
||||
data = msg[1]['data']
|
||||
assert data['method'] == 'update'
|
||||
assert data['state'] == {'ci': 5}
|
||||
|
||||
|
||||
# This test is disabled, meaning ipywidgets REQUIRES
|
||||
# any JSON received to format int-like numbers as ints
|
||||
def _x_test_set_state_int_to_int_like():
|
||||
# Note: Setting i to an int-like float will produce an
|
||||
# error, so if JSON producer were to always create
|
||||
# float formatted numbers, this would fail!
|
||||
|
||||
w = NumberWidget()
|
||||
# Set floats to int-like floats
|
||||
w.set_state(dict(
|
||||
i = 3.0
|
||||
))
|
||||
# Ensure no update message gets produced
|
||||
assert len(w.comm.messages) == 0
|
||||
|
||||
|
||||
def test_set_state_int_to_float():
|
||||
w = NumberWidget()
|
||||
|
||||
# Set Int to float
|
||||
with pytest.raises(TraitError):
|
||||
w.set_state(dict(
|
||||
i = 3.5
|
||||
))
|
||||
|
||||
def test_property_lock():
|
||||
# when this widget's value is set to 42, it sets itself to 2, and then back to 42 again (and then stops)
|
||||
class AnnoyingWidget(Widget):
|
||||
value = Float().tag(sync=True)
|
||||
stop = Bool(False)
|
||||
|
||||
@observe('value')
|
||||
def _propagate_value(self, change):
|
||||
print('_propagate_value', change.new)
|
||||
if self.stop:
|
||||
return
|
||||
if change.new == 42:
|
||||
self.value = 2
|
||||
if change.new == 2:
|
||||
self.stop = True
|
||||
self.value = 42
|
||||
|
||||
widget = AnnoyingWidget(value=1)
|
||||
assert widget.value == 1
|
||||
|
||||
widget._send = mock.MagicMock()
|
||||
# this mimics a value coming from the front end
|
||||
widget.set_state({'value': 42})
|
||||
assert widget.value == 42
|
||||
|
||||
# we expect first the {'value': 2.0} state to be send, followed by the {'value': 42.0} state
|
||||
msg = {'method': 'update', 'state': {'value': 2.0}, 'buffer_paths': []}
|
||||
call2 = mock.call(msg, buffers=[])
|
||||
|
||||
msg = {'method': 'update', 'state': {'value': 42.0}, 'buffer_paths': []}
|
||||
call42 = mock.call(msg, buffers=[])
|
||||
|
||||
calls = [call2, call42]
|
||||
widget._send.assert_has_calls(calls)
|
245
venv/Lib/site-packages/ipywidgets/widgets/tests/test_traits.py
Normal file
245
venv/Lib/site-packages/ipywidgets/widgets/tests/test_traits.py
Normal file
|
@ -0,0 +1,245 @@
|
|||
# Copyright (c) Jupyter Development Team.
|
||||
# Distributed under the terms of the Modified BSD License.
|
||||
|
||||
"""Test trait types of the widget packages."""
|
||||
import array
|
||||
import datetime as dt
|
||||
|
||||
import pytest
|
||||
|
||||
from unittest import TestCase
|
||||
from traitlets import HasTraits, Int, TraitError
|
||||
from traitlets.tests.test_traitlets import TraitTestBase
|
||||
|
||||
from ipywidgets import Color, NumberFormat
|
||||
from ipywidgets.widgets.widget import _remove_buffers, _put_buffers
|
||||
from ipywidgets.widgets.trait_types import date_serialization, TypedTuple
|
||||
|
||||
|
||||
class NumberFormatTrait(HasTraits):
|
||||
value = NumberFormat(".3f")
|
||||
|
||||
|
||||
class TestNumberFormat(TraitTestBase):
|
||||
obj = NumberFormatTrait()
|
||||
|
||||
_good_values = [
|
||||
'.2f', '.0%', '($.2f', '+20', '.^20', '.2s', '#x', ',.2r',
|
||||
' .2f', '.2', ''
|
||||
]
|
||||
_bad_values = [52, False, 'broken', '..2f', '.2a']
|
||||
|
||||
|
||||
class ColorTrait(HasTraits):
|
||||
value = Color("black")
|
||||
|
||||
|
||||
class TestColor(TraitTestBase):
|
||||
obj = ColorTrait()
|
||||
|
||||
|
||||
_good_values = [
|
||||
"blue", # valid color name
|
||||
"#AA0", # single digit hex
|
||||
"#FFFFFF", # double digit hex
|
||||
"transparent", # special color name
|
||||
'#aaaa', # single digit hex with alpha
|
||||
'#ffffffff', # double digit hex with alpha
|
||||
'rgb(0, 0, 0)', # rgb
|
||||
'rgb( 20,70,50 )', # rgb with spaces
|
||||
'rgba(10,10,10, 0.5)', # rgba with float alpha
|
||||
'rgba(255, 255, 255, 255)', # out of bounds alpha (spec says clamp to 1)
|
||||
'hsl(0.0, .0, 0)', # hsl
|
||||
'hsl( 0.5,0.3,0 )', # hsl with spaces
|
||||
'hsla(10,10,10, 0.5)', # rgba with float alpha
|
||||
]
|
||||
_bad_values = [
|
||||
"vanilla", "blues", # Invald color names
|
||||
1.2, 0.0, # Should fail with float input
|
||||
0, 1, 2, # Should fail with int input
|
||||
'rgb(0.4, 512, -40)',
|
||||
'hsl(0.4, 512, -40)',
|
||||
'rgba(0, 0, 0)',
|
||||
'hsla(0, 0, 0)',
|
||||
None,
|
||||
]
|
||||
|
||||
|
||||
class ColorTraitWithNone(HasTraits):
|
||||
value = Color("black", allow_none=True)
|
||||
|
||||
|
||||
class TestColorWithNone(TraitTestBase):
|
||||
obj = ColorTraitWithNone()
|
||||
|
||||
_good_values = TestColor._good_values + [None]
|
||||
_bad_values = list(filter(lambda v: v is not None, TestColor._bad_values))
|
||||
|
||||
|
||||
class TestDateSerialization(TestCase):
|
||||
|
||||
def setUp(self):
|
||||
self.to_json = date_serialization['to_json']
|
||||
self.dummy_manager = None
|
||||
|
||||
def test_serialize_none(self):
|
||||
self.assertIs(self.to_json(None, self.dummy_manager), None)
|
||||
|
||||
def test_serialize_date(self):
|
||||
date = dt.date(1900, 2, 18)
|
||||
expected = {
|
||||
'year': 1900,
|
||||
'month': 1,
|
||||
'date': 18
|
||||
}
|
||||
self.assertEqual(self.to_json(date, self.dummy_manager), expected)
|
||||
|
||||
|
||||
class TestDateDeserialization(TestCase):
|
||||
|
||||
def setUp(self):
|
||||
self.from_json = date_serialization['from_json']
|
||||
self.dummy_manager = None
|
||||
|
||||
def test_deserialize_none(self):
|
||||
self.assertIs(self.from_json(None, self.dummy_manager), None)
|
||||
|
||||
def test_deserialize_date(self):
|
||||
serialized_date = {
|
||||
'year': 1900,
|
||||
'month': 1,
|
||||
'date': 18
|
||||
}
|
||||
expected = dt.date(1900, 2, 18)
|
||||
self.assertEqual(
|
||||
self.from_json(serialized_date, self.dummy_manager),
|
||||
expected
|
||||
)
|
||||
|
||||
|
||||
class TestBuffers(TestCase):
|
||||
def test_remove_and_put_buffers(self):
|
||||
mv1 = memoryview(b'test1')
|
||||
mv2 = memoryview(b'test2')
|
||||
state = {'plain': [0, 'text'], # should not get removed
|
||||
'x': {'ar': mv1}, # should result in an empty dict
|
||||
'y': {'shape': (10, 10), 'data': mv1},
|
||||
'z': (mv1, mv2), # tests tuple assigment
|
||||
'top': mv1, # test a top level removal
|
||||
'deep': {'a': 1, 'b':[0,{'deeper':mv2}]}} # deeply nested
|
||||
plain = state['plain']
|
||||
x = state['x']
|
||||
y = state['y']
|
||||
y_shape = y['shape']
|
||||
state_before = state
|
||||
state, buffer_paths, buffers = _remove_buffers(state)
|
||||
|
||||
# check if buffers are removed
|
||||
self.assertIn('plain', state)
|
||||
self.assertIn('shape', state['y'])
|
||||
self.assertNotIn('ar', state['x'])
|
||||
self.assertEqual(state['x'], {})
|
||||
self.assertNotIn('data', state['y'])
|
||||
self.assertNotIn(mv1, state['z'])
|
||||
self.assertNotIn(mv1, state['z'])
|
||||
self.assertNotIn('top', state)
|
||||
self.assertIn('deep', state)
|
||||
self.assertIn('b', state['deep'])
|
||||
self.assertNotIn('deeper', state['deep']['b'][1])
|
||||
|
||||
# check that items that didn't need change aren't touched
|
||||
self.assertIsNot(state, state_before)
|
||||
self.assertIs(state['plain'], plain)
|
||||
self.assertIsNot(state['x'], x)
|
||||
self.assertIsNot(state['y'], y)
|
||||
self.assertIs(state['y']['shape'], y_shape)
|
||||
|
||||
# check that the buffer paths really point to the right buffer
|
||||
for path, buffer in [(['x', 'ar'], mv1), (['y', 'data'], mv1), (['z', 0], mv1), (['z', 1], mv2),\
|
||||
(['top'], mv1), (['deep', 'b', 1, 'deeper'], mv2)]:
|
||||
self.assertIn(path, buffer_paths, "%r not in path" % path)
|
||||
index = buffer_paths.index(path)
|
||||
self.assertEqual(buffer, buffers[index])
|
||||
|
||||
# and check that we can put it back together again
|
||||
_put_buffers(state, buffer_paths, buffers)
|
||||
# we know that tuples get converted to list, so help the comparison by changing the tuple to a list
|
||||
state_before['z'] = list(state_before['z'])
|
||||
self.assertEqual(state_before, state)
|
||||
|
||||
|
||||
|
||||
def test_typed_tuple_uninitialized_ints():
|
||||
class TestCase(HasTraits):
|
||||
value = TypedTuple(trait=Int())
|
||||
|
||||
obj = TestCase()
|
||||
assert obj.value == ()
|
||||
|
||||
|
||||
def test_typed_tuple_init_ints():
|
||||
class TestCase(HasTraits):
|
||||
value = TypedTuple(trait=Int())
|
||||
|
||||
obj = TestCase(value=(1, 2, 3))
|
||||
assert obj.value == (1, 2, 3)
|
||||
|
||||
|
||||
def test_typed_tuple_set_ints():
|
||||
class TestCase(HasTraits):
|
||||
value = TypedTuple(trait=Int())
|
||||
|
||||
obj = TestCase()
|
||||
obj.value = (1, 2, 3)
|
||||
assert obj.value == (1, 2, 3)
|
||||
|
||||
|
||||
def test_typed_tuple_default():
|
||||
class TestCase(HasTraits):
|
||||
value = TypedTuple(default_value=(1, 2, 3))
|
||||
|
||||
obj = TestCase()
|
||||
assert obj.value == (1, 2, 3)
|
||||
|
||||
|
||||
def test_typed_tuple_mixed_default():
|
||||
class TestCase(HasTraits):
|
||||
value = TypedTuple(default_value=(1, 2, 'foobar'))
|
||||
|
||||
obj = TestCase()
|
||||
assert obj.value == (1, 2, 'foobar')
|
||||
|
||||
|
||||
def test_typed_tuple_bad_default():
|
||||
class TestCase(HasTraits):
|
||||
value = TypedTuple(trait=Int(), default_value=(1, 2, 'foobar'))
|
||||
|
||||
|
||||
with pytest.raises(TraitError):
|
||||
obj = TestCase()
|
||||
a = obj.value # a read might be needed to trigger default validation
|
||||
|
||||
|
||||
def test_typed_tuple_bad_set():
|
||||
class TestCase(HasTraits):
|
||||
value = TypedTuple(trait=Int())
|
||||
|
||||
obj = TestCase()
|
||||
with pytest.raises(TraitError):
|
||||
obj.value = (1, 2, 'foobar')
|
||||
|
||||
|
||||
def test_typed_tuple_positional_trait():
|
||||
class TestCase(HasTraits):
|
||||
value = TypedTuple(Int())
|
||||
|
||||
obj = TestCase(value=(1, 2, 3))
|
||||
assert obj.value == (1, 2, 3)
|
||||
|
||||
|
||||
def test_typed_tuple_positional_default():
|
||||
class TestCase(HasTraits):
|
||||
value = TypedTuple((1, 2, 3))
|
||||
|
||||
obj = TestCase()
|
||||
assert obj.value == (1, 2, 3)
|
|
@ -0,0 +1,45 @@
|
|||
# Copyright (c) Jupyter Development Team.
|
||||
# Distributed under the terms of the Modified BSD License.
|
||||
|
||||
"""Test Widget."""
|
||||
|
||||
from IPython.core.interactiveshell import InteractiveShell
|
||||
from IPython.display import display
|
||||
from IPython.utils.capture import capture_output
|
||||
|
||||
from ..widget import Widget
|
||||
from ..widget_button import Button
|
||||
|
||||
|
||||
def test_no_widget_view():
|
||||
# ensure IPython shell is instantiated
|
||||
# otherwise display() just calls print
|
||||
shell = InteractiveShell.instance()
|
||||
|
||||
with capture_output() as cap:
|
||||
w = Widget()
|
||||
display(w)
|
||||
|
||||
assert len(cap.outputs) == 1, "expect 1 output"
|
||||
mime_bundle = cap.outputs[0].data
|
||||
assert mime_bundle['text/plain'] == repr(w), "expected plain text output"
|
||||
assert 'application/vnd.jupyter.widget-view+json' not in mime_bundle, "widget has no view"
|
||||
assert cap.stdout == '', repr(cap.stdout)
|
||||
assert cap.stderr == '', repr(cap.stderr)
|
||||
|
||||
|
||||
def test_widget_view():
|
||||
# ensure IPython shell is instantiated
|
||||
# otherwise display() just calls print
|
||||
shell = InteractiveShell.instance()
|
||||
|
||||
with capture_output() as cap:
|
||||
w = Button()
|
||||
display(w)
|
||||
|
||||
assert len(cap.outputs) == 1, "expect 1 output"
|
||||
mime_bundle = cap.outputs[0].data
|
||||
assert mime_bundle['text/plain'] == repr(w), "expected plain text output"
|
||||
assert 'application/vnd.jupyter.widget-view+json' in mime_bundle, "widget should have have a view"
|
||||
assert cap.stdout == '', repr(cap.stdout)
|
||||
assert cap.stderr == '', repr(cap.stderr)
|
|
@ -0,0 +1,33 @@
|
|||
# Copyright (c) Jupyter Development Team.
|
||||
# Distributed under the terms of the Modified BSD License.
|
||||
|
||||
from unittest import TestCase
|
||||
|
||||
from traitlets import TraitError
|
||||
|
||||
import ipywidgets as widgets
|
||||
|
||||
|
||||
class TestBox(TestCase):
|
||||
|
||||
def test_construction(self):
|
||||
box = widgets.Box()
|
||||
assert box.get_state()['children'] == []
|
||||
|
||||
def test_construction_with_children(self):
|
||||
html = widgets.HTML('some html')
|
||||
slider = widgets.IntSlider()
|
||||
box = widgets.Box([html, slider])
|
||||
children_state = box.get_state()['children']
|
||||
assert children_state == [
|
||||
widgets.widget._widget_to_json(html, None),
|
||||
widgets.widget._widget_to_json(slider, None),
|
||||
]
|
||||
|
||||
def test_construction_style(self):
|
||||
box = widgets.Box(box_style='warning')
|
||||
assert box.get_state()['box_style'] == 'warning'
|
||||
|
||||
def test_construction_invalid_style(self):
|
||||
with self.assertRaises(TraitError):
|
||||
widgets.Box(box_style='invalid')
|
|
@ -0,0 +1,22 @@
|
|||
# Copyright (c) Jupyter Development Team.
|
||||
# Distributed under the terms of the Modified BSD License.
|
||||
|
||||
from unittest import TestCase
|
||||
|
||||
from traitlets import TraitError
|
||||
|
||||
from ipywidgets import FloatSlider
|
||||
|
||||
|
||||
class TestFloatSlider(TestCase):
|
||||
|
||||
def test_construction(self):
|
||||
FloatSlider()
|
||||
|
||||
def test_construction_readout_format(self):
|
||||
slider = FloatSlider(readout_format='$.1f')
|
||||
assert slider.get_state()['readout_format'] == '$.1f'
|
||||
|
||||
def test_construction_invalid_readout_format(self):
|
||||
with self.assertRaises(TraitError):
|
||||
FloatSlider(readout_format='broken')
|
|
@ -0,0 +1,172 @@
|
|||
# Copyright (c) Jupyter Development Team.
|
||||
# Distributed under the terms of the Modified BSD License.
|
||||
|
||||
"""Test Image widget"""
|
||||
|
||||
import io
|
||||
import os
|
||||
|
||||
from ipywidgets import Image
|
||||
|
||||
import hashlib
|
||||
|
||||
import pkgutil
|
||||
|
||||
import tempfile
|
||||
from contextlib import contextmanager
|
||||
|
||||
# Data
|
||||
@contextmanager
|
||||
def get_logo_png():
|
||||
# Once the tests are not in the package, this context manager can be
|
||||
# replaced with the location of the actual file
|
||||
LOGO_DATA = pkgutil.get_data('ipywidgets.widgets.tests',
|
||||
'data/jupyter-logo-transparent.png')
|
||||
handle, fname = tempfile.mkstemp()
|
||||
os.close(handle)
|
||||
with open(fname, 'wb') as f:
|
||||
f.write(LOGO_DATA)
|
||||
|
||||
yield fname
|
||||
|
||||
os.remove(fname)
|
||||
|
||||
LOGO_PNG_DIGEST = '3ff9eafd7197083153e83339a72e7a335539bae189c33554c680e4382c98af02'
|
||||
|
||||
|
||||
def test_empty_image():
|
||||
# Empty images shouldn't raise any errors
|
||||
Image()
|
||||
|
||||
|
||||
def test_image_value():
|
||||
random_bytes = b'\x0ee\xca\x80\xcd\x9ak#\x7f\x07\x03\xa7'
|
||||
|
||||
Image(value=random_bytes)
|
||||
|
||||
|
||||
def test_image_format():
|
||||
# Test that these format names don't throw an error
|
||||
Image(format='png')
|
||||
|
||||
Image(format='jpeg')
|
||||
|
||||
Image(format='url')
|
||||
|
||||
|
||||
def test_from_filename():
|
||||
with get_logo_png() as LOGO_PNG:
|
||||
img = Image.from_file(LOGO_PNG)
|
||||
|
||||
assert_equal_hash(img.value, LOGO_PNG_DIGEST)
|
||||
|
||||
|
||||
def test_set_from_filename():
|
||||
img = Image()
|
||||
with get_logo_png() as LOGO_PNG:
|
||||
img.set_value_from_file(LOGO_PNG)
|
||||
|
||||
assert_equal_hash(img.value, LOGO_PNG_DIGEST)
|
||||
|
||||
|
||||
def test_from_file():
|
||||
with get_logo_png() as LOGO_PNG:
|
||||
with open(LOGO_PNG, 'rb') as f:
|
||||
img = Image.from_file(f)
|
||||
assert_equal_hash(img.value, LOGO_PNG_DIGEST)
|
||||
|
||||
|
||||
def test_set_value_from_file():
|
||||
img = Image()
|
||||
with get_logo_png() as LOGO_PNG:
|
||||
with open(LOGO_PNG, 'rb') as f:
|
||||
img.set_value_from_file(f)
|
||||
assert_equal_hash(img.value, LOGO_PNG_DIGEST)
|
||||
|
||||
|
||||
def test_from_url_unicode():
|
||||
img = Image.from_url(u'https://jupyter.org/assets/main-logo.svg')
|
||||
assert img.value == b'https://jupyter.org/assets/main-logo.svg'
|
||||
|
||||
|
||||
def test_from_url_bytes():
|
||||
img = Image.from_url(b'https://jupyter.org/assets/main-logo.svg')
|
||||
|
||||
assert img.value == b'https://jupyter.org/assets/main-logo.svg'
|
||||
|
||||
|
||||
def test_format_inference_filename():
|
||||
with tempfile.NamedTemporaryFile(suffix='.svg', delete=False) as f:
|
||||
name = f.name
|
||||
f.close() # Allow tests to run on Windows
|
||||
img = Image.from_file(name)
|
||||
|
||||
assert img.format == 'svg+xml'
|
||||
|
||||
|
||||
def test_format_inference_file():
|
||||
with tempfile.NamedTemporaryFile(suffix='.gif', delete=False) as f:
|
||||
img = Image.from_file(f)
|
||||
|
||||
assert img.format == 'gif'
|
||||
|
||||
|
||||
def test_format_inference_stream():
|
||||
# There's no way to infer the format, so it should default to png
|
||||
fstream = io.BytesIO(b'')
|
||||
img = Image.from_file(fstream)
|
||||
|
||||
assert img.format == 'png'
|
||||
|
||||
|
||||
def test_serialize():
|
||||
fstream = io.BytesIO(b'123')
|
||||
img = Image.from_file(fstream)
|
||||
|
||||
img_state = img.get_state()
|
||||
|
||||
# for python27 it is a memoryview
|
||||
assert isinstance(img_state['value'], (bytes, memoryview))
|
||||
# make sure it is (for python 3), since that is what it will be once it comes off the wire
|
||||
img_state['value'] = memoryview(img_state['value'])
|
||||
|
||||
# check that we can deserialize it and get back the original value
|
||||
img_copy = Image()
|
||||
img_copy.set_state(img_state)
|
||||
assert img.value == img_copy.value
|
||||
|
||||
|
||||
def test_format_inference_overridable():
|
||||
with tempfile.NamedTemporaryFile(suffix='.svg', delete=False) as f:
|
||||
name = f.name
|
||||
f.close() # Allow tests to run on Windows
|
||||
img = Image.from_file(name, format='gif')
|
||||
|
||||
assert img.format == 'gif'
|
||||
|
||||
|
||||
def test_value_repr_length():
|
||||
with get_logo_png() as LOGO_PNG:
|
||||
with open(LOGO_PNG, 'rb') as f:
|
||||
img = Image.from_file(f)
|
||||
assert len(img.__repr__()) < 120
|
||||
assert img.__repr__().endswith("...')")
|
||||
|
||||
|
||||
def test_value_repr_url():
|
||||
img = Image.from_url(b'https://jupyter.org/assets/main-logo.svg')
|
||||
|
||||
assert 'https://jupyter.org/assets/main-logo.svg' in img.__repr__()
|
||||
|
||||
|
||||
# Helper functions
|
||||
def get_hash_hex(byte_str):
|
||||
m = hashlib.new('sha256')
|
||||
|
||||
m.update(byte_str)
|
||||
|
||||
return m.hexdigest()
|
||||
|
||||
|
||||
def assert_equal_hash(byte_str, digest):
|
||||
assert get_hash_hex(byte_str) == digest
|
|
@ -0,0 +1,223 @@
|
|||
import sys
|
||||
from unittest import TestCase
|
||||
from contextlib import contextmanager
|
||||
|
||||
from IPython.display import Markdown, Image
|
||||
from ipywidgets import widget_output
|
||||
|
||||
|
||||
class TestOutputWidget(TestCase):
|
||||
|
||||
@contextmanager
|
||||
def _mocked_ipython(self, get_ipython, clear_output):
|
||||
""" Context manager that monkeypatches get_ipython and clear_output """
|
||||
original_clear_output = widget_output.clear_output
|
||||
original_get_ipython = widget_output.get_ipython
|
||||
widget_output.get_ipython = get_ipython
|
||||
widget_output.clear_output = clear_output
|
||||
try:
|
||||
yield
|
||||
finally:
|
||||
widget_output.clear_output = original_clear_output
|
||||
widget_output.get_ipython = original_get_ipython
|
||||
|
||||
def _mock_get_ipython(self, msg_id):
|
||||
""" Returns a mock IPython application with a mocked kernel """
|
||||
kernel = type(
|
||||
'mock_kernel',
|
||||
(object, ),
|
||||
{'_parent_header': {'header': {'msg_id': msg_id}}}
|
||||
)
|
||||
|
||||
# Specifically override this so the traceback
|
||||
# is still printed to screen
|
||||
def showtraceback(self_, exc_tuple, *args, **kwargs):
|
||||
etype, evalue, tb = exc_tuple
|
||||
raise etype(evalue)
|
||||
|
||||
ipython = type(
|
||||
'mock_ipython',
|
||||
(object, ),
|
||||
{'kernel': kernel, 'showtraceback': showtraceback}
|
||||
)
|
||||
return ipython
|
||||
|
||||
def _mock_clear_output(self):
|
||||
""" Mock function that records calls to it """
|
||||
calls = []
|
||||
|
||||
def clear_output(*args, **kwargs):
|
||||
calls.append((args, kwargs))
|
||||
clear_output.calls = calls
|
||||
|
||||
return clear_output
|
||||
|
||||
def test_set_msg_id_when_capturing(self):
|
||||
msg_id = 'msg-id'
|
||||
get_ipython = self._mock_get_ipython(msg_id)
|
||||
clear_output = self._mock_clear_output()
|
||||
|
||||
with self._mocked_ipython(get_ipython, clear_output):
|
||||
widget = widget_output.Output()
|
||||
assert widget.msg_id == ''
|
||||
with widget:
|
||||
assert widget.msg_id == msg_id
|
||||
assert widget.msg_id == ''
|
||||
|
||||
def test_clear_output(self):
|
||||
msg_id = 'msg-id'
|
||||
get_ipython = self._mock_get_ipython(msg_id)
|
||||
clear_output = self._mock_clear_output()
|
||||
|
||||
with self._mocked_ipython(get_ipython, clear_output):
|
||||
widget = widget_output.Output()
|
||||
widget.clear_output(wait=True)
|
||||
|
||||
assert len(clear_output.calls) == 1
|
||||
assert clear_output.calls[0] == ((), {'wait': True})
|
||||
|
||||
def test_capture_decorator(self):
|
||||
msg_id = 'msg-id'
|
||||
get_ipython = self._mock_get_ipython(msg_id)
|
||||
clear_output = self._mock_clear_output()
|
||||
expected_argument = 'arg'
|
||||
expected_keyword_argument = True
|
||||
captee_calls = []
|
||||
|
||||
with self._mocked_ipython(get_ipython, clear_output):
|
||||
widget = widget_output.Output()
|
||||
assert widget.msg_id == ''
|
||||
|
||||
@widget.capture()
|
||||
def captee(*args, **kwargs):
|
||||
# Check that we are capturing output
|
||||
assert widget.msg_id == msg_id
|
||||
|
||||
# Check that arguments are passed correctly
|
||||
captee_calls.append((args, kwargs))
|
||||
|
||||
captee(
|
||||
expected_argument, keyword_argument=expected_keyword_argument)
|
||||
assert widget.msg_id == ''
|
||||
captee()
|
||||
|
||||
assert len(captee_calls) == 2
|
||||
assert captee_calls[0] == (
|
||||
(expected_argument, ),
|
||||
{'keyword_argument': expected_keyword_argument}
|
||||
)
|
||||
assert captee_calls[1] == ((), {})
|
||||
|
||||
def test_capture_decorator_clear_output(self):
|
||||
msg_id = 'msg-id'
|
||||
get_ipython = self._mock_get_ipython(msg_id)
|
||||
clear_output = self._mock_clear_output()
|
||||
|
||||
with self._mocked_ipython(get_ipython, clear_output):
|
||||
widget = widget_output.Output()
|
||||
|
||||
@widget.capture(clear_output=True, wait=True)
|
||||
def captee(*args, **kwargs):
|
||||
# Check that we are capturing output
|
||||
assert widget.msg_id == msg_id
|
||||
|
||||
captee()
|
||||
captee()
|
||||
|
||||
assert len(clear_output.calls) == 2
|
||||
assert clear_output.calls[0] == clear_output.calls[1] == \
|
||||
((), {'wait': True})
|
||||
|
||||
def test_capture_decorator_no_clear_output(self):
|
||||
msg_id = 'msg-id'
|
||||
get_ipython = self._mock_get_ipython(msg_id)
|
||||
clear_output = self._mock_clear_output()
|
||||
|
||||
with self._mocked_ipython(get_ipython, clear_output):
|
||||
widget = widget_output.Output()
|
||||
|
||||
@widget.capture(clear_output=False)
|
||||
def captee(*args, **kwargs):
|
||||
# Check that we are capturing output
|
||||
assert widget.msg_id == msg_id
|
||||
|
||||
captee()
|
||||
captee()
|
||||
|
||||
assert len(clear_output.calls) == 0
|
||||
|
||||
|
||||
def _make_stream_output(text, name):
|
||||
return {
|
||||
'output_type': 'stream',
|
||||
'name': name,
|
||||
'text': text
|
||||
}
|
||||
|
||||
|
||||
def test_append_stdout():
|
||||
widget = widget_output.Output()
|
||||
|
||||
# Try appending a message to stdout.
|
||||
widget.append_stdout("snakes!")
|
||||
expected = (_make_stream_output("snakes!", "stdout"),)
|
||||
assert widget.outputs == expected, repr(widget.outputs)
|
||||
|
||||
# Try appending a second message.
|
||||
widget.append_stdout("more snakes!")
|
||||
expected += (_make_stream_output("more snakes!", "stdout"),)
|
||||
assert widget.outputs == expected, repr(widget.outputs)
|
||||
|
||||
|
||||
def test_append_stderr():
|
||||
widget = widget_output.Output()
|
||||
|
||||
# Try appending a message to stderr.
|
||||
widget.append_stderr("snakes!")
|
||||
expected = (_make_stream_output("snakes!", "stderr"),)
|
||||
assert widget.outputs == expected, repr(widget.outputs)
|
||||
|
||||
# Try appending a second message.
|
||||
widget.append_stderr("more snakes!")
|
||||
expected += (_make_stream_output("more snakes!", "stderr"),)
|
||||
assert widget.outputs == expected, repr(widget.outputs)
|
||||
|
||||
|
||||
def test_append_display_data():
|
||||
widget = widget_output.Output()
|
||||
|
||||
# Try appending a Markdown object.
|
||||
widget.append_display_data(Markdown("# snakes!"))
|
||||
expected = (
|
||||
{
|
||||
'output_type': 'display_data',
|
||||
'data': {
|
||||
'text/plain': '<IPython.core.display.Markdown object>',
|
||||
'text/markdown': '# snakes!'
|
||||
},
|
||||
'metadata': {}
|
||||
},
|
||||
)
|
||||
assert widget.outputs == expected, repr(widget.outputs)
|
||||
|
||||
# Now try appending an Image.
|
||||
image_data = b"foobar"
|
||||
image_data_b64 = image_data if sys.version_info[0] < 3 else 'Zm9vYmFy\n'
|
||||
|
||||
widget.append_display_data(Image(image_data, width=123, height=456))
|
||||
expected += (
|
||||
{
|
||||
'output_type': 'display_data',
|
||||
'data': {
|
||||
'image/png': image_data_b64,
|
||||
'text/plain': '<IPython.core.display.Image object>'
|
||||
},
|
||||
'metadata': {
|
||||
'image/png': {
|
||||
'width': 123,
|
||||
'height': 456
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
assert widget.outputs == expected, repr(widget.outputs)
|
|
@ -0,0 +1,96 @@
|
|||
# Copyright (c) Jupyter Development Team.
|
||||
# Distributed under the terms of the Modified BSD License.
|
||||
|
||||
import inspect
|
||||
import warnings
|
||||
from unittest import TestCase
|
||||
|
||||
from traitlets import TraitError
|
||||
|
||||
from ipywidgets import Dropdown, SelectionSlider, Select
|
||||
|
||||
|
||||
class TestDropdown(TestCase):
|
||||
|
||||
def test_construction(self):
|
||||
Dropdown()
|
||||
|
||||
def test_deprecation_warning_mapping_options(self):
|
||||
with warnings.catch_warnings(record=True) as w:
|
||||
warnings.simplefilter("always")
|
||||
|
||||
# Clearing the internal __warningregistry__ seems to be required for
|
||||
# Python 2 (but not for Python 3)
|
||||
module = inspect.getmodule(Dropdown)
|
||||
getattr(module, '__warningregistry__', {}).clear()
|
||||
|
||||
Dropdown(options={'One': 1, 'Two': 2, 'Three': 3})
|
||||
assert len(w) > 0
|
||||
assert issubclass(w[-1].category, DeprecationWarning)
|
||||
assert "Support for mapping types has been deprecated" in str(w[-1].message)
|
||||
|
||||
|
||||
class TestSelectionSlider(TestCase):
|
||||
|
||||
def test_construction(self):
|
||||
SelectionSlider(options=['a', 'b', 'c'])
|
||||
|
||||
def test_index_trigger(self):
|
||||
slider = SelectionSlider(options=['a', 'b', 'c'])
|
||||
observations = []
|
||||
def f(change):
|
||||
observations.append(change.new)
|
||||
slider.observe(f, 'index')
|
||||
assert slider.index == 0
|
||||
slider.options = [4, 5, 6]
|
||||
assert slider.index == 0
|
||||
assert slider.value == 4
|
||||
assert slider.label == '4'
|
||||
assert observations == [0]
|
||||
|
||||
class TestSelection(TestCase):
|
||||
|
||||
def test_construction(self):
|
||||
select = Select(options=['a', 'b', 'c'])
|
||||
|
||||
def test_index_trigger(self):
|
||||
select = Select(options=[1, 2, 3])
|
||||
observations = []
|
||||
def f(change):
|
||||
observations.append(change.new)
|
||||
select.observe(f, 'index')
|
||||
assert select.index == 0
|
||||
select.options = [4, 5, 6]
|
||||
assert select.index == 0
|
||||
assert select.value == 4
|
||||
assert select.label == '4'
|
||||
assert observations == [0]
|
||||
|
||||
def test_duplicate(self):
|
||||
select = Select(options=['first', 1, 'dup', 'dup'])
|
||||
observations = []
|
||||
def f(change):
|
||||
observations.append(change.new)
|
||||
select.observe(f, 'index')
|
||||
select.index = 3
|
||||
assert select.index == 3
|
||||
assert select.value == 'dup'
|
||||
assert select.label == 'dup'
|
||||
assert observations == [3]
|
||||
select.index = 2
|
||||
assert select.index == 2
|
||||
assert select.value == 'dup'
|
||||
assert select.label == 'dup'
|
||||
assert observations == [3, 2]
|
||||
select.index = 0
|
||||
assert select.index == 0
|
||||
assert select.value == 'first'
|
||||
assert select.label == 'first'
|
||||
assert observations == [3, 2, 0]
|
||||
|
||||
# picks the first matching value
|
||||
select.value = 'dup'
|
||||
assert select.index == 2
|
||||
assert select.value == 'dup'
|
||||
assert select.label == 'dup'
|
||||
assert observations == [3, 2, 0, 2]
|
|
@ -0,0 +1,34 @@
|
|||
# Copyright (c) Jupyter Development Team.
|
||||
# Distributed under the terms of the Modified BSD License.
|
||||
|
||||
from ..widget_string import Combobox
|
||||
|
||||
|
||||
def test_combobox_creation_blank():
|
||||
w = Combobox()
|
||||
assert w.value == ''
|
||||
assert w.options == ()
|
||||
assert w.ensure_option == False
|
||||
|
||||
|
||||
def test_combobox_creation_kwargs():
|
||||
w = Combobox(
|
||||
value='Chocolate',
|
||||
options=[
|
||||
"Chocolate",
|
||||
"Coconut",
|
||||
"Mint",
|
||||
"Strawberry",
|
||||
"Vanilla",
|
||||
],
|
||||
ensure_option=True
|
||||
)
|
||||
assert w.value == 'Chocolate'
|
||||
assert w.options == (
|
||||
"Chocolate",
|
||||
"Coconut",
|
||||
"Mint",
|
||||
"Strawberry",
|
||||
"Vanilla",
|
||||
)
|
||||
assert w.ensure_option == True
|
|
@ -0,0 +1,718 @@
|
|||
"Testing widget layout templates"
|
||||
|
||||
from unittest import TestCase
|
||||
|
||||
try:
|
||||
from unittest import mock
|
||||
except ImportError:
|
||||
import mock
|
||||
|
||||
import pytest
|
||||
|
||||
import traitlets
|
||||
import ipywidgets as widgets
|
||||
from ipywidgets.widgets.widget_templates import LayoutProperties
|
||||
|
||||
class TestTwoByTwoLayout(TestCase):
|
||||
"""test layout templates"""
|
||||
|
||||
def test_merge_cells(self): #pylint: disable=no-self-use
|
||||
"""test merging cells with missing widgets"""
|
||||
|
||||
button1 = widgets.Button()
|
||||
button2 = widgets.Button()
|
||||
button3 = widgets.Button()
|
||||
button4 = widgets.Button()
|
||||
|
||||
box = widgets.TwoByTwoLayout(top_left=button1,
|
||||
top_right=button2,
|
||||
bottom_left=button3,
|
||||
bottom_right=button4)
|
||||
|
||||
assert box.layout.grid_template_areas == ('"top-left top-right"\n' +
|
||||
'"bottom-left bottom-right"')
|
||||
assert box.top_left.layout.grid_area == 'top-left'
|
||||
assert box.top_right.layout.grid_area == 'top-right'
|
||||
assert box.bottom_left.layout.grid_area == 'bottom-left'
|
||||
assert box.bottom_right.layout.grid_area == 'bottom-right'
|
||||
assert len(box.get_state()['children']) == 4
|
||||
|
||||
box = widgets.TwoByTwoLayout(top_left=button1,
|
||||
top_right=button2,
|
||||
bottom_left=None,
|
||||
bottom_right=button4)
|
||||
|
||||
assert box.layout.grid_template_areas == ('"top-left top-right"\n' +
|
||||
'"top-left bottom-right"')
|
||||
assert box.top_left.layout.grid_area == 'top-left'
|
||||
assert box.top_right.layout.grid_area == 'top-right'
|
||||
assert box.bottom_left is None
|
||||
assert box.bottom_right.layout.grid_area == 'bottom-right'
|
||||
assert len(box.get_state()['children']) == 3
|
||||
|
||||
box = widgets.TwoByTwoLayout(top_left=None,
|
||||
top_right=button2,
|
||||
bottom_left=button3,
|
||||
bottom_right=button4)
|
||||
|
||||
assert box.layout.grid_template_areas == ('"bottom-left top-right"\n' +
|
||||
'"bottom-left bottom-right"')
|
||||
assert box.top_left is None
|
||||
assert box.top_right.layout.grid_area == 'top-right'
|
||||
assert box.bottom_left.layout.grid_area == 'bottom-left'
|
||||
assert box.bottom_right.layout.grid_area == 'bottom-right'
|
||||
assert len(box.get_state()['children']) == 3
|
||||
|
||||
box = widgets.TwoByTwoLayout(top_left=None,
|
||||
top_right=button2,
|
||||
bottom_left=None,
|
||||
bottom_right=button4)
|
||||
|
||||
assert box.layout.grid_template_areas == ('"top-right top-right"\n' +
|
||||
'"bottom-right bottom-right"')
|
||||
assert box.top_left is None
|
||||
assert box.top_right.layout.grid_area == 'top-right'
|
||||
assert box.bottom_left is None
|
||||
assert box.bottom_right.layout.grid_area == 'bottom-right'
|
||||
assert len(box.get_state()['children']) == 2
|
||||
|
||||
box = widgets.TwoByTwoLayout(top_left=button1,
|
||||
top_right=None,
|
||||
bottom_left=button3,
|
||||
bottom_right=button4)
|
||||
|
||||
assert box.layout.grid_template_areas == ('"top-left bottom-right"\n' +
|
||||
'"bottom-left bottom-right"')
|
||||
assert box.top_left.layout.grid_area == 'top-left'
|
||||
assert box.top_right is None
|
||||
assert box.bottom_left.layout.grid_area == 'bottom-left'
|
||||
assert box.bottom_right.layout.grid_area == 'bottom-right'
|
||||
assert len(box.get_state()['children']) == 3
|
||||
|
||||
|
||||
box = widgets.TwoByTwoLayout(top_left=button1,
|
||||
top_right=None,
|
||||
bottom_left=None,
|
||||
bottom_right=None)
|
||||
|
||||
assert box.layout.grid_template_areas == ('"top-left top-left"\n' +
|
||||
'"top-left top-left"')
|
||||
|
||||
assert box.top_left is button1
|
||||
assert box.top_left.layout.grid_area == 'top-left'
|
||||
assert box.top_right is None
|
||||
assert box.bottom_left is None
|
||||
assert box.bottom_right is None
|
||||
assert len(box.get_state()['children']) == 1
|
||||
|
||||
box = widgets.TwoByTwoLayout(top_left=None,
|
||||
top_right=button1,
|
||||
bottom_left=None,
|
||||
bottom_right=None)
|
||||
|
||||
assert box.layout.grid_template_areas == ('"top-right top-right"\n' +
|
||||
'"top-right top-right"')
|
||||
|
||||
assert box.top_right is button1
|
||||
assert box.top_right.layout.grid_area == 'top-right'
|
||||
assert box.top_left is None
|
||||
assert box.bottom_left is None
|
||||
assert box.bottom_right is None
|
||||
assert len(box.get_state()['children']) == 1
|
||||
|
||||
box = widgets.TwoByTwoLayout(top_left=None,
|
||||
top_right=None,
|
||||
bottom_left=None,
|
||||
bottom_right=None)
|
||||
|
||||
assert box.layout.grid_template_areas is None
|
||||
assert box.top_left is None
|
||||
assert box.top_right is None
|
||||
assert box.bottom_left is None
|
||||
assert box.bottom_right is None
|
||||
assert not box.get_state()['children']
|
||||
|
||||
box = widgets.TwoByTwoLayout(top_left=None,
|
||||
top_right=button1,
|
||||
bottom_left=None,
|
||||
bottom_right=None,
|
||||
merge=False)
|
||||
|
||||
assert box.layout.grid_template_areas == ('"top-left top-right"\n' +
|
||||
'"bottom-left bottom-right"')
|
||||
|
||||
assert box.top_right is button1
|
||||
assert box.top_right.layout.grid_area == 'top-right'
|
||||
assert box.top_left is None
|
||||
assert box.bottom_left is None
|
||||
assert box.bottom_right is None
|
||||
assert len(box.get_state()['children']) == 1
|
||||
|
||||
def test_keep_layout_options(self): #pylint: disable=no-self-use
|
||||
"""test whether layout options are passed down to GridBox"""
|
||||
|
||||
layout = widgets.Layout(align_items="center")
|
||||
button1 = widgets.Button()
|
||||
button2 = widgets.Button()
|
||||
button3 = widgets.Button()
|
||||
button4 = widgets.Button()
|
||||
|
||||
box = widgets.TwoByTwoLayout(top_left=button1, top_right=button2,
|
||||
bottom_left=button3, bottom_right=button4,
|
||||
layout=layout)
|
||||
|
||||
assert box.layout.align_items == 'center'
|
||||
|
||||
def test_pass_layout_options(self): #pylint: disable=no-self-use
|
||||
"""test whether the extra layout options of the template class are
|
||||
passed down to Layout object"""
|
||||
|
||||
button1 = widgets.Button()
|
||||
button2 = widgets.Button()
|
||||
button3 = widgets.Button()
|
||||
button4 = widgets.Button()
|
||||
|
||||
box = widgets.TwoByTwoLayout(top_left=button1, top_right=button2,
|
||||
bottom_left=button3, bottom_right=button4,
|
||||
grid_gap="10px", justify_content="center",
|
||||
align_items="center")
|
||||
|
||||
assert box.layout.grid_gap == "10px"
|
||||
assert box.layout.justify_content == "center"
|
||||
assert box.layout.align_items == "center"
|
||||
|
||||
# we still should be able to pass them through layout
|
||||
layout = widgets.Layout(grid_gap="10px", justify_content="center",
|
||||
align_items="center")
|
||||
box = widgets.TwoByTwoLayout(top_left=button1, top_right=button2,
|
||||
bottom_left=button3, bottom_right=button4,
|
||||
layout=layout
|
||||
)
|
||||
|
||||
assert box.layout.grid_gap == "10px"
|
||||
assert box.layout.justify_content == "center"
|
||||
assert box.layout.align_items == "center"
|
||||
|
||||
# values passed directly in the constructor should overwite layout options
|
||||
layout = widgets.Layout(grid_gap="10px", justify_content="center",
|
||||
align_items="center")
|
||||
box = widgets.TwoByTwoLayout(top_left=button1, top_right=button2,
|
||||
bottom_left=button3, bottom_right=button4,
|
||||
layout=layout, grid_gap="30px"
|
||||
)
|
||||
|
||||
assert box.layout.grid_gap == "30px"
|
||||
assert box.layout.justify_content == "center"
|
||||
assert box.layout.align_items == "center"
|
||||
|
||||
|
||||
@mock.patch("ipywidgets.Layout.send_state")
|
||||
def test_update_dynamically(self, send_state): #pylint: disable=no-self-use
|
||||
"""test whether it's possible to add widget outside __init__"""
|
||||
|
||||
button1 = widgets.Button()
|
||||
button2 = widgets.Button()
|
||||
button3 = widgets.Button()
|
||||
button4 = widgets.Button()
|
||||
|
||||
box = widgets.TwoByTwoLayout(top_left=button1, top_right=button3,
|
||||
bottom_left=None, bottom_right=button4)
|
||||
from ipykernel.kernelbase import Kernel
|
||||
|
||||
state = box.get_state()
|
||||
assert len(state['children']) == 3
|
||||
assert box.layout.grid_template_areas == ('"top-left top-right"\n' +
|
||||
'"top-left bottom-right"')
|
||||
|
||||
box.layout.comm.kernel = mock.MagicMock(spec=Kernel) #for mocking purposes
|
||||
send_state.reset_mock()
|
||||
box.bottom_left = button2
|
||||
|
||||
state = box.get_state()
|
||||
assert len(state['children']) == 4
|
||||
assert box.layout.grid_template_areas == ('"top-left top-right"\n' +
|
||||
'"bottom-left bottom-right"')
|
||||
# check whether frontend was informed
|
||||
send_state.assert_called_once_with(key="grid_template_areas")
|
||||
|
||||
box = widgets.TwoByTwoLayout(top_left=button1, top_right=button3,
|
||||
bottom_left=None, bottom_right=button4)
|
||||
assert box.layout.grid_template_areas == ('"top-left top-right"\n' +
|
||||
'"top-left bottom-right"')
|
||||
box.layout.comm.kernel = mock.MagicMock(spec=Kernel) #for mocking purposes
|
||||
send_state.reset_mock()
|
||||
box.merge = False
|
||||
assert box.layout.grid_template_areas == ('"top-left top-right"\n' +
|
||||
'"bottom-left bottom-right"')
|
||||
send_state.assert_called_once_with(key="grid_template_areas")
|
||||
|
||||
|
||||
class TestAppLayout(TestCase):
|
||||
"""test layout templates"""
|
||||
|
||||
def test_create_with_defaults(self):
|
||||
"test creating with default values"
|
||||
|
||||
footer = widgets.Button()
|
||||
header = widgets.Button()
|
||||
center = widgets.Button()
|
||||
left_sidebar = widgets.Button()
|
||||
right_sidebar = widgets.Button()
|
||||
|
||||
box = widgets.AppLayout(
|
||||
footer=footer,
|
||||
header=header,
|
||||
center=center,
|
||||
left_sidebar=left_sidebar,
|
||||
right_sidebar=right_sidebar
|
||||
)
|
||||
|
||||
assert box.layout.grid_template_areas == ('"header header header"\n' +
|
||||
'"left-sidebar center right-sidebar"\n' +
|
||||
'"footer footer footer"')
|
||||
assert box.footer.layout.grid_area == 'footer'
|
||||
assert box.header.layout.grid_area == 'header'
|
||||
assert box.center.layout.grid_area == 'center'
|
||||
assert box.left_sidebar.layout.grid_area == 'left-sidebar'
|
||||
assert box.right_sidebar.layout.grid_area == 'right-sidebar'
|
||||
|
||||
assert len(box.get_state()['children']) == 5
|
||||
|
||||
# empty layout should produce no effects
|
||||
|
||||
box = widgets.AppLayout()
|
||||
assert box.layout.grid_template_areas is None
|
||||
assert box.layout.grid_template_columns is None
|
||||
assert box.layout.grid_template_rows is None
|
||||
assert len(box.get_state()['children']) == 0
|
||||
|
||||
|
||||
def test_merge_empty_cells(self):
|
||||
"test if cells are correctly merged"
|
||||
|
||||
footer = widgets.Button()
|
||||
header = widgets.Button()
|
||||
center = widgets.Button()
|
||||
left_sidebar = widgets.Button()
|
||||
right_sidebar = widgets.Button()
|
||||
|
||||
# merge all if only one widget
|
||||
box = widgets.AppLayout(
|
||||
center=center
|
||||
)
|
||||
|
||||
assert box.layout.grid_template_areas == ('"center center center"\n' +
|
||||
'"center center center"\n' +
|
||||
'"center center center"')
|
||||
assert box.center.layout.grid_area == 'center'
|
||||
|
||||
assert len(box.get_state()['children']) == 1
|
||||
|
||||
box = widgets.AppLayout(
|
||||
left_sidebar=left_sidebar
|
||||
)
|
||||
|
||||
assert box.layout.grid_template_areas == ('"left-sidebar left-sidebar left-sidebar"\n' +
|
||||
'"left-sidebar left-sidebar left-sidebar"\n' +
|
||||
'"left-sidebar left-sidebar left-sidebar"')
|
||||
assert box.left_sidebar.layout.grid_area == 'left-sidebar'
|
||||
|
||||
assert len(box.get_state()['children']) == 1
|
||||
|
||||
# merge left and right sidebars with center
|
||||
|
||||
box = widgets.AppLayout(
|
||||
header=header,
|
||||
footer=footer,
|
||||
left_sidebar=left_sidebar,
|
||||
center=center
|
||||
)
|
||||
|
||||
assert box.layout.grid_template_areas == ('"header header header"\n' +
|
||||
'"left-sidebar center center"\n' +
|
||||
'"footer footer footer"')
|
||||
assert box.footer.layout.grid_area == 'footer'
|
||||
assert box.header.layout.grid_area == 'header'
|
||||
assert box.center.layout.grid_area == 'center'
|
||||
assert box.left_sidebar.layout.grid_area == 'left-sidebar'
|
||||
assert len(box.get_state()['children']) == 4
|
||||
|
||||
box = widgets.AppLayout(
|
||||
header=header,
|
||||
footer=footer,
|
||||
right_sidebar=right_sidebar,
|
||||
center=center
|
||||
)
|
||||
|
||||
assert box.layout.grid_template_areas == ('"header header header"\n' +
|
||||
'"center center right-sidebar"\n' +
|
||||
'"footer footer footer"')
|
||||
assert box.footer.layout.grid_area == 'footer'
|
||||
assert box.header.layout.grid_area == 'header'
|
||||
assert box.center.layout.grid_area == 'center'
|
||||
assert box.right_sidebar.layout.grid_area == 'right-sidebar'
|
||||
assert len(box.get_state()['children']) == 4
|
||||
|
||||
box = widgets.AppLayout(
|
||||
header=header,
|
||||
footer=footer,
|
||||
center=center
|
||||
)
|
||||
|
||||
assert box.layout.grid_template_areas == ('"header header header"\n' +
|
||||
'"center center center"\n' +
|
||||
'"footer footer footer"')
|
||||
assert box.footer.layout.grid_area == 'footer'
|
||||
assert box.header.layout.grid_area == 'header'
|
||||
assert box.center.layout.grid_area == 'center'
|
||||
assert len(box.get_state()['children']) == 3
|
||||
|
||||
# if only center missing, remove it from view
|
||||
box = widgets.AppLayout(
|
||||
header=header,
|
||||
footer=footer,
|
||||
center=None,
|
||||
left_sidebar=left_sidebar,
|
||||
right_sidebar=right_sidebar
|
||||
)
|
||||
|
||||
assert box.layout.grid_template_areas == ('"header header"\n' +
|
||||
'"left-sidebar right-sidebar"\n' +
|
||||
'"footer footer"')
|
||||
assert box.footer.layout.grid_area == 'footer'
|
||||
assert box.header.layout.grid_area == 'header'
|
||||
assert box.left_sidebar.layout.grid_area == 'left-sidebar'
|
||||
assert box.right_sidebar.layout.grid_area == 'right-sidebar'
|
||||
assert box.center is None
|
||||
assert len(box.get_state()['children']) == 4
|
||||
|
||||
# center and one sidebar missing -> 3 row arrangement
|
||||
box = widgets.AppLayout(
|
||||
header=header,
|
||||
footer=footer,
|
||||
center=None,
|
||||
left_sidebar=None,
|
||||
right_sidebar=right_sidebar
|
||||
)
|
||||
|
||||
assert box.layout.grid_template_areas == ('"header header"\n' +
|
||||
'"right-sidebar right-sidebar"\n' +
|
||||
'"footer footer"')
|
||||
assert box.footer.layout.grid_area == 'footer'
|
||||
assert box.header.layout.grid_area == 'header'
|
||||
assert box.left_sidebar is None
|
||||
assert box.right_sidebar.layout.grid_area == 'right-sidebar'
|
||||
assert box.center is None
|
||||
assert len(box.get_state()['children']) == 3
|
||||
|
||||
|
||||
# remove middle row is both sidebars and center missing
|
||||
box = widgets.AppLayout(
|
||||
header=header,
|
||||
footer=footer,
|
||||
center=None,
|
||||
left_sidebar=None,
|
||||
right_sidebar=None
|
||||
)
|
||||
|
||||
assert box.layout.grid_template_areas == ('"header"\n' +
|
||||
'"footer"')
|
||||
assert box.footer.layout.grid_area == 'footer'
|
||||
assert box.header.layout.grid_area == 'header'
|
||||
assert box.center is None
|
||||
assert box.left_sidebar is None
|
||||
assert box.right_sidebar is None
|
||||
assert len(box.get_state()['children']) == 2
|
||||
|
||||
|
||||
|
||||
# do not merge if merge=False
|
||||
box = widgets.AppLayout(
|
||||
header=header,
|
||||
footer=footer,
|
||||
center=center,
|
||||
merge=False
|
||||
)
|
||||
|
||||
assert box.layout.grid_template_areas == ('"header header header"\n' +
|
||||
'"left-sidebar center right-sidebar"\n' +
|
||||
'"footer footer footer"')
|
||||
assert box.footer.layout.grid_area == 'footer'
|
||||
assert box.header.layout.grid_area == 'header'
|
||||
assert box.center.layout.grid_area == 'center'
|
||||
assert box.left_sidebar is None
|
||||
assert box.right_sidebar is None
|
||||
assert len(box.get_state()['children']) == 3
|
||||
|
||||
# merge header and footer simply removes it from view
|
||||
box = widgets.AppLayout(
|
||||
footer=footer,
|
||||
center=center,
|
||||
left_sidebar=left_sidebar,
|
||||
right_sidebar=right_sidebar
|
||||
)
|
||||
|
||||
assert box.layout.grid_template_areas == ('"left-sidebar center right-sidebar"\n' +
|
||||
'"footer footer footer"')
|
||||
assert box.center.layout.grid_area == 'center'
|
||||
assert box.left_sidebar.layout.grid_area == 'left-sidebar'
|
||||
assert box.right_sidebar.layout.grid_area == 'right-sidebar'
|
||||
assert box.footer.layout.grid_area == 'footer'
|
||||
assert box.header is None
|
||||
assert len(box.get_state()['children']) == 4
|
||||
|
||||
box = widgets.AppLayout(
|
||||
header=header,
|
||||
center=center,
|
||||
left_sidebar=left_sidebar,
|
||||
right_sidebar=right_sidebar
|
||||
)
|
||||
|
||||
assert box.layout.grid_template_areas == ('"header header header"\n' +
|
||||
'"left-sidebar center right-sidebar"')
|
||||
assert box.center.layout.grid_area == 'center'
|
||||
assert box.left_sidebar.layout.grid_area == 'left-sidebar'
|
||||
assert box.right_sidebar.layout.grid_area == 'right-sidebar'
|
||||
assert box.header.layout.grid_area == 'header'
|
||||
assert box.footer is None
|
||||
assert len(box.get_state()['children']) == 4
|
||||
|
||||
box = widgets.AppLayout(
|
||||
center=center,
|
||||
left_sidebar=left_sidebar,
|
||||
right_sidebar=right_sidebar
|
||||
)
|
||||
|
||||
assert box.layout.grid_template_areas == '"left-sidebar center right-sidebar"'
|
||||
assert box.center.layout.grid_area == 'center'
|
||||
assert box.left_sidebar.layout.grid_area == 'left-sidebar'
|
||||
assert box.right_sidebar.layout.grid_area == 'right-sidebar'
|
||||
assert box.footer is None
|
||||
assert box.header is None
|
||||
assert len(box.get_state()['children']) == 3
|
||||
|
||||
# merge all if only one widget
|
||||
box = widgets.AppLayout(
|
||||
center=center
|
||||
)
|
||||
|
||||
assert box.layout.grid_template_areas == ('"center center center"\n' +
|
||||
'"center center center"\n' +
|
||||
'"center center center"')
|
||||
assert box.center.layout.grid_area == 'center'
|
||||
|
||||
assert len(box.get_state()['children']) == 1
|
||||
|
||||
def test_size_to_css(self):
|
||||
|
||||
box = widgets.AppLayout()
|
||||
assert box._size_to_css("100px") == '100px'
|
||||
assert box._size_to_css("1fr") == '1fr'
|
||||
assert box._size_to_css("2.5fr") == '2.5fr'
|
||||
assert box._size_to_css('2.5') == '2.5fr'
|
||||
assert box._size_to_css('25%') == '25%'
|
||||
|
||||
with pytest.raises(TypeError):
|
||||
box._size_to_css('this is not correct size')
|
||||
|
||||
|
||||
def test_set_pane_widths_heights(self):
|
||||
|
||||
footer = widgets.Button()
|
||||
header = widgets.Button()
|
||||
center = widgets.Button()
|
||||
left_sidebar = widgets.Button()
|
||||
right_sidebar = widgets.Button()
|
||||
|
||||
box = widgets.AppLayout(
|
||||
header=header,
|
||||
footer=footer,
|
||||
left_sidebar=left_sidebar,
|
||||
right_sidebar=left_sidebar,
|
||||
center=center
|
||||
)
|
||||
|
||||
with pytest.raises(traitlets.TraitError):
|
||||
box.pane_widths = ['1fx', '1fx', '1fx', '1fx']
|
||||
with pytest.raises(traitlets.TraitError):
|
||||
box.pane_widths = ['1fx', '1fx']
|
||||
|
||||
with pytest.raises(traitlets.TraitError):
|
||||
box.pane_heights = ['1fx', '1fx', '1fx', '1fx']
|
||||
with pytest.raises(traitlets.TraitError):
|
||||
box.pane_heights = ['1fx', '1fx']
|
||||
|
||||
assert box.layout.grid_template_rows == "1fr 3fr 1fr"
|
||||
assert box.layout.grid_template_columns == "1fr 2fr 1fr"
|
||||
|
||||
box.pane_heights = ['3fr', '100px', 20]
|
||||
assert box.layout.grid_template_rows == "3fr 100px 20fr"
|
||||
assert box.layout.grid_template_columns == "1fr 2fr 1fr"
|
||||
|
||||
box.pane_widths = [3, 3, 1]
|
||||
assert box.layout.grid_template_rows == "3fr 100px 20fr"
|
||||
assert box.layout.grid_template_columns == "3fr 3fr 1fr"
|
||||
|
||||
class TestGridspecLayout(TestCase):
|
||||
"test GridspecLayout"
|
||||
|
||||
def test_init(self):
|
||||
with pytest.raises(traitlets.TraitError):
|
||||
box = widgets.GridspecLayout()
|
||||
|
||||
with pytest.raises(traitlets.TraitError):
|
||||
box = widgets.GridspecLayout(n_rows=-1, n_columns=1)
|
||||
|
||||
box = widgets.GridspecLayout(n_rows=5, n_columns=3)
|
||||
assert box.n_rows == 5
|
||||
assert box.n_columns == 3
|
||||
assert len(box._grid_template_areas) == 5
|
||||
assert len(box._grid_template_areas[0]) == 3
|
||||
|
||||
box = widgets.GridspecLayout(1, 2)
|
||||
assert box.n_rows == 1
|
||||
assert box.n_columns == 2
|
||||
|
||||
with pytest.raises(traitlets.TraitError):
|
||||
box = widgets.GridspecLayout(0, 0)
|
||||
|
||||
def test_setitem_index(self):
|
||||
|
||||
box = widgets.GridspecLayout(2, 3)
|
||||
button1 = widgets.Button()
|
||||
button2 = widgets.Button()
|
||||
button3 = widgets.Button()
|
||||
button4 = widgets.Button()
|
||||
|
||||
box[0, 0] = button1
|
||||
button1_label = button1.layout.grid_area
|
||||
assert button1 in box.children
|
||||
assert box.layout.grid_template_areas == '''"{} . ."\n". . ."'''.format(button1_label)
|
||||
|
||||
box[-1, -1] = button2
|
||||
button2_label = button2.layout.grid_area
|
||||
assert button1_label != button2_label
|
||||
assert button2 in box.children
|
||||
assert box.layout.grid_template_areas == '''"{} . ."\n". . {}"'''.format(button1_label,
|
||||
button2_label)
|
||||
|
||||
box[1, 0] = button3
|
||||
button3_label = button3.layout.grid_area
|
||||
assert button1_label != button3_label
|
||||
assert button2_label != button3_label
|
||||
assert button3 in box.children
|
||||
assert box.layout.grid_template_areas == '''"{b1} . ."\n"{b3} . {b2}"'''.format(b1=button1_label,
|
||||
b2=button2_label,
|
||||
b3=button3_label)
|
||||
|
||||
#replace widget
|
||||
box[1, 0] = button4
|
||||
button4_label = button4.layout.grid_area
|
||||
assert button1_label != button4_label
|
||||
assert button2_label != button4_label
|
||||
assert button4 in box.children
|
||||
assert button3 not in box.children
|
||||
|
||||
assert box.layout.grid_template_areas == '''"{b1} . ."\n"{b4} . {b2}"'''.format(b1=button1_label,
|
||||
b2=button2_label,
|
||||
b4=button4_label)
|
||||
|
||||
def test_setitem_slices(self):
|
||||
|
||||
box = widgets.GridspecLayout(2, 3)
|
||||
button1 = widgets.Button()
|
||||
|
||||
box[:2, 0] = button1
|
||||
assert len(box.children) == 1
|
||||
assert button1 in box.children
|
||||
button1_label = button1.layout.grid_area
|
||||
|
||||
assert box.layout.grid_template_areas == '''"{b1} . ."\n"{b1} . ."'''.format(b1=button1_label)
|
||||
|
||||
box = widgets.GridspecLayout(2, 3)
|
||||
button1 = widgets.Button()
|
||||
button2 = widgets.Button()
|
||||
|
||||
box[:2, 1:] = button1
|
||||
assert len(box.children) == 1
|
||||
assert button1 in box.children
|
||||
button1_label = button1.layout.grid_area
|
||||
|
||||
assert box.layout.grid_template_areas == '''". {b1} {b1}"\n". {b1} {b1}"'''.format(b1=button1_label)
|
||||
|
||||
# replace button
|
||||
box[:2, 1:] = button2
|
||||
assert len(box.children) == 1
|
||||
assert button2 in box.children
|
||||
button2_label = button2.layout.grid_area
|
||||
|
||||
assert box.layout.grid_template_areas == '''". {b1} {b1}"\n". {b1} {b1}"'''.format(b1=button2_label)
|
||||
|
||||
def test_getitem_index(self):
|
||||
"test retrieving widget"
|
||||
|
||||
box = widgets.GridspecLayout(2, 3)
|
||||
button1 = widgets.Button()
|
||||
|
||||
box[0, 0] = button1
|
||||
|
||||
assert box[0, 0] is button1
|
||||
|
||||
def test_getitem_slices(self):
|
||||
"test retrieving widgets with slices"
|
||||
|
||||
box = widgets.GridspecLayout(2, 3)
|
||||
button1 = widgets.Button()
|
||||
|
||||
box[:2, 0] = button1
|
||||
assert box[:2, 0] is button1
|
||||
|
||||
box = widgets.GridspecLayout(2, 3)
|
||||
button1 = widgets.Button()
|
||||
button2 = widgets.Button()
|
||||
|
||||
box[0, 0] = button1
|
||||
box[1, 0] = button2
|
||||
assert box[0, 0] is button1
|
||||
assert box[1, 0] is button2
|
||||
|
||||
with pytest.raises(TypeError, match="The slice spans"):
|
||||
button = box[:2, 0]
|
||||
|
||||
|
||||
class TestLayoutProperties(TestCase):
|
||||
"""test mixin with layout properties"""
|
||||
|
||||
class DummyTemplate(widgets.GridBox, LayoutProperties):
|
||||
location = traitlets.Instance(widgets.Widget, allow_none=True)
|
||||
|
||||
def test_layout_updated_on_trait_change(self):
|
||||
"test whether respective layout traits are updated when traits change"
|
||||
|
||||
template = self.DummyTemplate(width="100%")
|
||||
assert template.width == '100%'
|
||||
assert template.layout.width == '100%'
|
||||
|
||||
template.width = 'auto'
|
||||
assert template.width == 'auto'
|
||||
assert template.layout.width == 'auto'
|
||||
|
||||
def test_align_items_extra_options(self):
|
||||
|
||||
template = self.DummyTemplate(align_items='top')
|
||||
assert template.align_items == 'top'
|
||||
assert template.layout.align_items == 'flex-start'
|
||||
|
||||
template.align_items = 'bottom'
|
||||
assert template.align_items == 'bottom'
|
||||
assert template.layout.align_items == 'flex-end'
|
||||
|
||||
def test_validate_properties(self):
|
||||
|
||||
prop_obj = self.DummyTemplate()
|
||||
for prop in LayoutProperties.align_items.values:
|
||||
prop_obj.align_items = prop
|
||||
assert prop_obj.align_items == prop
|
||||
|
||||
with pytest.raises(traitlets.TraitError):
|
||||
prop_obj.align_items = 'any default position'
|
|
@ -0,0 +1,26 @@
|
|||
# Copyright (c) Jupyter Development Team.
|
||||
# Distributed under the terms of the Modified BSD License.
|
||||
|
||||
from unittest import TestCase
|
||||
|
||||
from traitlets import TraitError
|
||||
|
||||
from ipywidgets import FileUpload
|
||||
|
||||
|
||||
class TestFileUpload(TestCase):
|
||||
|
||||
def test_construction(self):
|
||||
uploader = FileUpload()
|
||||
# Default
|
||||
assert uploader.accept == ''
|
||||
assert not uploader.multiple
|
||||
assert not uploader.disabled
|
||||
|
||||
def test_construction_with_params(self):
|
||||
uploader = FileUpload(accept='.txt',
|
||||
multiple=True,
|
||||
disabled=True)
|
||||
assert uploader.accept == '.txt'
|
||||
assert uploader.multiple
|
||||
assert uploader.disabled
|
47
venv/Lib/site-packages/ipywidgets/widgets/tests/utils.py
Normal file
47
venv/Lib/site-packages/ipywidgets/widgets/tests/utils.py
Normal file
|
@ -0,0 +1,47 @@
|
|||
# Copyright (c) Jupyter Development Team.
|
||||
# Distributed under the terms of the Modified BSD License.
|
||||
|
||||
from ipykernel.comm import Comm
|
||||
from ipywidgets import Widget
|
||||
|
||||
class DummyComm(Comm):
|
||||
comm_id = 'a-b-c-d'
|
||||
kernel = 'Truthy'
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(DummyComm, self).__init__(*args, **kwargs)
|
||||
self.messages = []
|
||||
|
||||
def open(self, *args, **kwargs):
|
||||
pass
|
||||
|
||||
def send(self, *args, **kwargs):
|
||||
self.messages.append((args, kwargs))
|
||||
|
||||
def close(self, *args, **kwargs):
|
||||
pass
|
||||
|
||||
_widget_attrs = {}
|
||||
undefined = object()
|
||||
|
||||
def setup_test_comm():
|
||||
_widget_attrs['_comm_default'] = getattr(Widget, '_comm_default', undefined)
|
||||
Widget._comm_default = lambda self: DummyComm()
|
||||
_widget_attrs['_ipython_display_'] = Widget._ipython_display_
|
||||
def raise_not_implemented(*args, **kwargs):
|
||||
raise NotImplementedError()
|
||||
Widget._ipython_display_ = raise_not_implemented
|
||||
|
||||
def teardown_test_comm():
|
||||
for attr, value in _widget_attrs.items():
|
||||
if value is undefined:
|
||||
delattr(Widget, attr)
|
||||
else:
|
||||
setattr(Widget, attr, value)
|
||||
_widget_attrs.clear()
|
||||
|
||||
def setup():
|
||||
setup_test_comm()
|
||||
|
||||
def teardown():
|
||||
teardown_test_comm()
|
224
venv/Lib/site-packages/ipywidgets/widgets/trait_types.py
Normal file
224
venv/Lib/site-packages/ipywidgets/widgets/trait_types.py
Normal file
|
@ -0,0 +1,224 @@
|
|||
# Copyright (c) Jupyter Development Team.
|
||||
# Distributed under the terms of the Modified BSD License.
|
||||
|
||||
"""
|
||||
Trait types for html widgets.
|
||||
"""
|
||||
|
||||
import re
|
||||
import traitlets
|
||||
import datetime as dt
|
||||
|
||||
from .util import string_types
|
||||
|
||||
|
||||
_color_names = ['aliceblue', 'antiquewhite', 'aqua', 'aquamarine', 'azure', 'beige', 'bisque', 'black', 'blanchedalmond', 'blue', 'blueviolet', 'brown', 'burlywood', 'cadetblue', 'chartreuse', 'chocolate', 'coral', 'cornflowerblue', 'cornsilk', 'crimson', 'cyan', 'darkblue', 'darkcyan', 'darkgoldenrod', 'darkgray', 'darkgreen', 'darkkhaki', 'darkmagenta', 'darkolivegreen', 'darkorange', 'darkorchid', 'darkred', 'darksalmon', 'darkseagreen', 'darkslateblue', 'darkslategray', 'darkturquoise', 'darkviolet', 'deeppink', 'deepskyblue', 'dimgray', 'dodgerblue', 'firebrick', 'floralwhite', 'forestgreen', 'fuchsia', 'gainsboro', 'ghostwhite', 'gold', 'goldenrod', 'gray', 'green', 'greenyellow', 'honeydew', 'hotpink', 'indianred ', 'indigo ', 'ivory', 'khaki', 'lavender', 'lavenderblush', 'lawngreen', 'lemonchiffon', 'lightblue', 'lightcoral', 'lightcyan', 'lightgoldenrodyellow', 'lightgray', 'lightgreen', 'lightpink', 'lightsalmon', 'lightseagreen', 'lightskyblue', 'lightslategray', 'lightsteelblue', 'lightyellow', 'lime', 'limegreen', 'linen', 'magenta', 'maroon', 'mediumaquamarine', 'mediumblue', 'mediumorchid', 'mediumpurple', 'mediumseagreen', 'mediumslateblue', 'mediumspringgreen', 'mediumturquoise', 'mediumvioletred', 'midnightblue', 'mintcream', 'mistyrose', 'moccasin', 'navajowhite', 'navy', 'oldlace', 'olive', 'olivedrab', 'orange', 'orangered', 'orchid', 'palegoldenrod', 'palegreen', 'paleturquoise', 'palevioletred', 'papayawhip', 'peachpuff', 'peru', 'pink', 'plum', 'powderblue', 'purple', 'rebeccapurple', 'red', 'rosybrown', 'royalblue', 'saddlebrown', 'salmon', 'sandybrown', 'seagreen', 'seashell', 'sienna', 'silver', 'skyblue', 'slateblue', 'slategray', 'snow', 'springgreen', 'steelblue', 'tan', 'teal', 'thistle', 'tomato', 'transparent', 'turquoise', 'violet', 'wheat', 'white', 'whitesmoke', 'yellow', 'yellowgreen']
|
||||
|
||||
# Regex colors #fff and #ffffff
|
||||
_color_hex_re = re.compile(r'#[a-fA-F0-9]{3}(?:[a-fA-F0-9]{3})?$')
|
||||
# Regex colors #ffff and #ffffffff (includes alpha value)
|
||||
_color_hexa_re = re.compile(r'^#[a-fA-F0-9]{4}(?:[a-fA-F0-9]{4})?$')
|
||||
|
||||
# Helpers (float percent, int percent with optional surrounding whitespace)
|
||||
_color_frac_percent = r'\s*(\d+(\.\d*)?|\.\d+)?%?\s*'
|
||||
_color_int_percent = r'\s*\d+%?\s*'
|
||||
|
||||
# rgb(), rgba(), hsl() and hsla() format strings
|
||||
_color_rgb = r'rgb\({ip},{ip},{ip}\)'
|
||||
_color_rgba = r'rgba\({ip},{ip},{ip},{fp}\)'
|
||||
_color_hsl = r'hsl\({fp},{fp},{fp}\)'
|
||||
_color_hsla = r'hsla\({fp},{fp},{fp},{fp}\)'
|
||||
|
||||
# Regex colors rgb/rgba/hsl/hsla
|
||||
_color_rgbhsl_re = re.compile('({0})|({1})|({2})|({3})'.format(
|
||||
_color_rgb, _color_rgba, _color_hsl, _color_hsla
|
||||
).format(ip=_color_int_percent, fp=_color_frac_percent))
|
||||
|
||||
|
||||
class Color(traitlets.Unicode):
|
||||
"""A string holding a valid HTML color such as 'blue', '#060482', '#A80'"""
|
||||
|
||||
info_text = 'a valid HTML color'
|
||||
default_value = traitlets.Undefined
|
||||
|
||||
def validate(self, obj, value):
|
||||
if value is None and self.allow_none:
|
||||
return value
|
||||
if isinstance(value, string_types):
|
||||
if (value.lower() in _color_names or _color_hex_re.match(value) or
|
||||
_color_hexa_re.match(value) or _color_rgbhsl_re.match(value)):
|
||||
return value
|
||||
|
||||
self.error(obj, value)
|
||||
|
||||
|
||||
class Datetime(traitlets.TraitType):
|
||||
"""A trait type holding a Python datetime object"""
|
||||
|
||||
klass = dt.datetime
|
||||
default_value = dt.datetime(1900, 1, 1)
|
||||
|
||||
|
||||
class Date(traitlets.TraitType):
|
||||
"""A trait type holding a Python date object"""
|
||||
|
||||
klass = dt.date
|
||||
default_value = dt.date(1900, 1, 1)
|
||||
|
||||
|
||||
def datetime_to_json(pydt, manager):
|
||||
"""Serialize a Python datetime object to json.
|
||||
|
||||
Instantiating a JavaScript Date object with a string assumes that the
|
||||
string is a UTC string, while instantiating it with constructor arguments
|
||||
assumes that it's in local time:
|
||||
|
||||
>>> cdate = new Date('2015-05-12')
|
||||
Mon May 11 2015 20:00:00 GMT-0400 (Eastern Daylight Time)
|
||||
>>> cdate = new Date(2015, 4, 12) // Months are 0-based indices in JS
|
||||
Tue May 12 2015 00:00:00 GMT-0400 (Eastern Daylight Time)
|
||||
|
||||
Attributes of this dictionary are to be passed to the JavaScript Date
|
||||
constructor.
|
||||
"""
|
||||
if pydt is None:
|
||||
return None
|
||||
else:
|
||||
return dict(
|
||||
year=pydt.year,
|
||||
month=pydt.month - 1, # Months are 0-based indices in JS
|
||||
date=pydt.day,
|
||||
hours=pydt.hour, # Hours, Minutes, Seconds and Milliseconds
|
||||
minutes=pydt.minute, # are plural in JS
|
||||
seconds=pydt.second,
|
||||
milliseconds=pydt.microsecond / 1000
|
||||
)
|
||||
|
||||
|
||||
def datetime_from_json(js, manager):
|
||||
"""Deserialize a Python datetime object from json."""
|
||||
if js is None:
|
||||
return None
|
||||
else:
|
||||
return dt.datetime(
|
||||
js['year'],
|
||||
js['month'] + 1, # Months are 1-based in Python
|
||||
js['date'],
|
||||
js['hours'],
|
||||
js['minutes'],
|
||||
js['seconds'],
|
||||
js['milliseconds'] * 1000
|
||||
)
|
||||
|
||||
datetime_serialization = {
|
||||
'from_json': datetime_from_json,
|
||||
'to_json': datetime_to_json
|
||||
}
|
||||
|
||||
|
||||
def date_to_json(pydate, manager):
|
||||
"""Serialize a Python date object.
|
||||
|
||||
Attributes of this dictionary are to be passed to the JavaScript Date
|
||||
constructor.
|
||||
"""
|
||||
if pydate is None:
|
||||
return None
|
||||
else:
|
||||
return dict(
|
||||
year=pydate.year,
|
||||
month=pydate.month - 1, # Months are 0-based indices in JS
|
||||
date=pydate.day
|
||||
)
|
||||
|
||||
|
||||
def date_from_json(js, manager):
|
||||
"""Deserialize a Javascript date."""
|
||||
if js is None:
|
||||
return None
|
||||
else:
|
||||
return dt.date(
|
||||
js['year'],
|
||||
js['month'] + 1, # Months are 1-based in Python
|
||||
js['date'],
|
||||
)
|
||||
|
||||
date_serialization = {
|
||||
'from_json': date_from_json,
|
||||
'to_json': date_to_json
|
||||
}
|
||||
|
||||
|
||||
class InstanceDict(traitlets.Instance):
|
||||
"""An instance trait which coerces a dict to an instance.
|
||||
|
||||
This lets the instance be specified as a dict, which is used
|
||||
to initialize the instance.
|
||||
|
||||
Also, we default to a trivial instance, even if args and kwargs
|
||||
is not specified."""
|
||||
|
||||
def validate(self, obj, value):
|
||||
if isinstance(value, dict):
|
||||
return super(InstanceDict, self).validate(obj, self.klass(**value))
|
||||
else:
|
||||
return super(InstanceDict, self).validate(obj, value)
|
||||
|
||||
def make_dynamic_default(self):
|
||||
return self.klass(*(self.default_args or ()),
|
||||
**(self.default_kwargs or {}))
|
||||
|
||||
|
||||
# The regexp is taken
|
||||
# from https://github.com/d3/d3-format/blob/master/src/formatSpecifier.js
|
||||
_number_format_re = re.compile(r'^(?:(.)?([<>=^]))?([+\-\( ])?([$#])?(0)?(\d+)?(,)?(\.\d+)?([a-z%])?$', re.I)
|
||||
|
||||
# The valid types are taken from
|
||||
# https://github.com/d3/d3-format/blob/master/src/formatTypes.js
|
||||
_number_format_types = {
|
||||
'e', 'f', 'g', 'r', 's', '%', 'p', 'b', 'o', 'd', 'x',
|
||||
'X', 'c', ''
|
||||
}
|
||||
|
||||
|
||||
class NumberFormat(traitlets.Unicode):
|
||||
"""A string holding a number format specifier, e.g. '.3f'
|
||||
|
||||
This traitlet holds a string that can be passed to the
|
||||
`d3-format <https://github.com/d3/d3-format>`_ JavaScript library.
|
||||
The format allowed is similar to the Python format specifier (PEP 3101).
|
||||
"""
|
||||
info_text = 'a valid number format'
|
||||
default_value = traitlets.Undefined
|
||||
|
||||
def validate(self, obj, value):
|
||||
value = super(NumberFormat, self).validate(obj, value)
|
||||
re_match = _number_format_re.match(value)
|
||||
if re_match is None:
|
||||
self.error(obj, value)
|
||||
else:
|
||||
format_type = re_match.group(9)
|
||||
if format_type is None:
|
||||
return value
|
||||
elif format_type in _number_format_types:
|
||||
return value
|
||||
else:
|
||||
raise traitlets.TraitError(
|
||||
'The type specifier of a NumberFormat trait must '
|
||||
'be one of {}, but a value of \'{}\' was '
|
||||
'specified.'.format(
|
||||
list(_number_format_types), format_type)
|
||||
)
|
||||
|
||||
class TypedTuple(traitlets.Container):
|
||||
"""A trait for a tuple of any length with type-checked elements."""
|
||||
klass = tuple
|
||||
_cast_types = (list,)
|
||||
|
||||
|
||||
def bytes_from_json(js, obj):
|
||||
return None if js is None else js.tobytes()
|
||||
|
||||
bytes_serialization = {
|
||||
'from_json': bytes_from_json,
|
||||
}
|
15
venv/Lib/site-packages/ipywidgets/widgets/util.py
Normal file
15
venv/Lib/site-packages/ipywidgets/widgets/util.py
Normal file
|
@ -0,0 +1,15 @@
|
|||
# Copyright (c) Jupyter Development Team.
|
||||
# Distributed under the terms of the Modified BSD License.
|
||||
|
||||
# six is not a direct dependency of this module
|
||||
# This replicates six.text_type
|
||||
try:
|
||||
text_type = unicode
|
||||
except NameError:
|
||||
text_type = str
|
||||
|
||||
|
||||
try:
|
||||
string_types = basestring
|
||||
except NameError:
|
||||
string_types = str
|
27
venv/Lib/site-packages/ipywidgets/widgets/valuewidget.py
Normal file
27
venv/Lib/site-packages/ipywidgets/widgets/valuewidget.py
Normal file
|
@ -0,0 +1,27 @@
|
|||
# Copyright (c) Jupyter Development Team.
|
||||
# Distributed under the terms of the Modified BSD License.
|
||||
|
||||
"""Contains the ValueWidget class"""
|
||||
|
||||
from .widget import Widget
|
||||
from traitlets import Any
|
||||
|
||||
|
||||
class ValueWidget(Widget):
|
||||
"""Widget that can be used for the input of an interactive function"""
|
||||
|
||||
value = Any(help="The value of the widget.")
|
||||
|
||||
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
|
||||
|
||||
def _repr_keys(self):
|
||||
# Ensure value key comes first, and is always present
|
||||
yield 'value'
|
||||
for key in super(ValueWidget, self)._repr_keys():
|
||||
if key != 'value':
|
||||
yield key
|
763
venv/Lib/site-packages/ipywidgets/widgets/widget.py
Normal file
763
venv/Lib/site-packages/ipywidgets/widgets/widget.py
Normal file
|
@ -0,0 +1,763 @@
|
|||
# coding: utf-8
|
||||
|
||||
# Copyright (c) Jupyter Development Team.
|
||||
# Distributed under the terms of the Modified BSD License.
|
||||
|
||||
"""Base Widget class. Allows user to create widgets in the back-end that render
|
||||
in the IPython notebook front-end.
|
||||
"""
|
||||
|
||||
from contextlib import contextmanager
|
||||
try:
|
||||
from collections.abc import Iterable
|
||||
except ImportError:
|
||||
# Python 2.7
|
||||
from collections import Iterable
|
||||
|
||||
from IPython.core.getipython import get_ipython
|
||||
from ipykernel.comm import Comm
|
||||
from traitlets import (
|
||||
HasTraits, Unicode, Dict, Instance, List, Int, Set, Bytes, observe, default, Container,
|
||||
Undefined)
|
||||
from ipython_genutils.py3compat import string_types, PY3
|
||||
from IPython.display import display
|
||||
from json import loads as jsonloads, dumps as jsondumps
|
||||
|
||||
from base64 import standard_b64encode
|
||||
|
||||
from .._version import __protocol_version__, __jupyter_widgets_base_version__
|
||||
PROTOCOL_VERSION_MAJOR = __protocol_version__.split('.')[0]
|
||||
|
||||
def _widget_to_json(x, obj):
|
||||
if isinstance(x, dict):
|
||||
return {k: _widget_to_json(v, obj) for k, v in x.items()}
|
||||
elif isinstance(x, (list, tuple)):
|
||||
return [_widget_to_json(v, obj) for v in x]
|
||||
elif isinstance(x, Widget):
|
||||
return "IPY_MODEL_" + x.model_id
|
||||
else:
|
||||
return x
|
||||
|
||||
def _json_to_widget(x, obj):
|
||||
if isinstance(x, dict):
|
||||
return {k: _json_to_widget(v, obj) for k, v in x.items()}
|
||||
elif isinstance(x, (list, tuple)):
|
||||
return [_json_to_widget(v, obj) for v in x]
|
||||
elif isinstance(x, string_types) and x.startswith('IPY_MODEL_') and x[10:] in Widget.widgets:
|
||||
return Widget.widgets[x[10:]]
|
||||
else:
|
||||
return x
|
||||
|
||||
widget_serialization = {
|
||||
'from_json': _json_to_widget,
|
||||
'to_json': _widget_to_json
|
||||
}
|
||||
|
||||
if PY3:
|
||||
_binary_types = (memoryview, bytearray, bytes)
|
||||
else:
|
||||
_binary_types = (memoryview, bytearray)
|
||||
|
||||
def _put_buffers(state, buffer_paths, buffers):
|
||||
"""The inverse of _remove_buffers, except here we modify the existing dict/lists.
|
||||
Modifying should be fine, since this is used when state comes from the wire.
|
||||
"""
|
||||
for buffer_path, buffer in zip(buffer_paths, buffers):
|
||||
# we'd like to set say sync_data['x'][0]['y'] = buffer
|
||||
# where buffer_path in this example would be ['x', 0, 'y']
|
||||
obj = state
|
||||
for key in buffer_path[:-1]:
|
||||
obj = obj[key]
|
||||
obj[buffer_path[-1]] = buffer
|
||||
|
||||
def _separate_buffers(substate, path, buffer_paths, buffers):
|
||||
"""For internal, see _remove_buffers"""
|
||||
# remove binary types from dicts and lists, but keep track of their paths
|
||||
# any part of the dict/list that needs modification will be cloned, so the original stays untouched
|
||||
# e.g. {'x': {'ar': ar}, 'y': [ar2, ar3]}, where ar/ar2/ar3 are binary types
|
||||
# will result in {'x': {}, 'y': [None, None]}, [ar, ar2, ar3], [['x', 'ar'], ['y', 0], ['y', 1]]
|
||||
# instead of removing elements from the list, this will make replacing the buffers on the js side much easier
|
||||
if isinstance(substate, (list, tuple)):
|
||||
is_cloned = False
|
||||
for i, v in enumerate(substate):
|
||||
if isinstance(v, _binary_types):
|
||||
if not is_cloned:
|
||||
substate = list(substate) # shallow clone list/tuple
|
||||
is_cloned = True
|
||||
substate[i] = None
|
||||
buffers.append(v)
|
||||
buffer_paths.append(path + [i])
|
||||
elif isinstance(v, (dict, list, tuple)):
|
||||
vnew = _separate_buffers(v, path + [i], buffer_paths, buffers)
|
||||
if v is not vnew: # only assign when value changed
|
||||
if not is_cloned:
|
||||
substate = list(substate) # clone list/tuple
|
||||
is_cloned = True
|
||||
substate[i] = vnew
|
||||
elif isinstance(substate, dict):
|
||||
is_cloned = False
|
||||
for k, v in substate.items():
|
||||
if isinstance(v, _binary_types):
|
||||
if not is_cloned:
|
||||
substate = dict(substate) # shallow clone dict
|
||||
is_cloned = True
|
||||
del substate[k]
|
||||
buffers.append(v)
|
||||
buffer_paths.append(path + [k])
|
||||
elif isinstance(v, (dict, list, tuple)):
|
||||
vnew = _separate_buffers(v, path + [k], buffer_paths, buffers)
|
||||
if v is not vnew: # only assign when value changed
|
||||
if not is_cloned:
|
||||
substate = dict(substate) # clone list/tuple
|
||||
is_cloned = True
|
||||
substate[k] = vnew
|
||||
else:
|
||||
raise ValueError("expected state to be a list or dict, not %r" % substate)
|
||||
return substate
|
||||
|
||||
def _remove_buffers(state):
|
||||
"""Return (state_without_buffers, buffer_paths, buffers) for binary message parts
|
||||
|
||||
A binary message part is a memoryview, bytearray, or python 3 bytes object.
|
||||
|
||||
As an example:
|
||||
>>> state = {'plain': [0, 'text'], 'x': {'ar': memoryview(ar1)}, 'y': {'shape': (10,10), 'data': memoryview(ar2)}}
|
||||
>>> _remove_buffers(state)
|
||||
({'plain': [0, 'text']}, {'x': {}, 'y': {'shape': (10, 10)}}, [['x', 'ar'], ['y', 'data']],
|
||||
[<memory at 0x107ffec48>, <memory at 0x107ffed08>])
|
||||
"""
|
||||
buffer_paths, buffers = [], []
|
||||
state = _separate_buffers(state, [], buffer_paths, buffers)
|
||||
return state, buffer_paths, buffers
|
||||
|
||||
def _buffer_list_equal(a, b):
|
||||
"""Compare two lists of buffers for equality.
|
||||
|
||||
Used to decide whether two sequences of buffers (memoryviews,
|
||||
bytearrays, or python 3 bytes) differ, such that a sync is needed.
|
||||
|
||||
Returns True if equal, False if unequal
|
||||
"""
|
||||
if len(a) != len(b):
|
||||
return False
|
||||
if a == b:
|
||||
return True
|
||||
for ia, ib in zip(a, b):
|
||||
# Check byte equality, since bytes are what is actually synced
|
||||
# NOTE: Simple ia != ib does not always work as intended, as
|
||||
# e.g. memoryview(np.frombuffer(ia, dtype='float32')) !=
|
||||
# memoryview(np.frombuffer(b)), since the format info differs.
|
||||
if PY3:
|
||||
# compare without copying
|
||||
if memoryview(ia).cast('B') != memoryview(ib).cast('B'):
|
||||
return False
|
||||
else:
|
||||
# python 2 doesn't have memoryview.cast, so we may have to copy
|
||||
if isinstance(ia, memoryview) and ia.format != 'B':
|
||||
ia = ia.tobytes()
|
||||
if isinstance(ib, memoryview) and ib.format != 'B':
|
||||
ib = ib.tobytes()
|
||||
if ia != ib:
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
class LoggingHasTraits(HasTraits):
|
||||
"""A parent class for HasTraits that log.
|
||||
Subclasses have a log trait, and the default behavior
|
||||
is to get the logger from the currently running Application.
|
||||
"""
|
||||
log = Instance('logging.Logger')
|
||||
@default('log')
|
||||
def _log_default(self):
|
||||
from traitlets import log
|
||||
return log.get_logger()
|
||||
|
||||
|
||||
class CallbackDispatcher(LoggingHasTraits):
|
||||
"""A structure for registering and running callbacks"""
|
||||
callbacks = List()
|
||||
|
||||
def __call__(self, *args, **kwargs):
|
||||
"""Call all of the registered callbacks."""
|
||||
value = None
|
||||
for callback in self.callbacks:
|
||||
try:
|
||||
local_value = callback(*args, **kwargs)
|
||||
except Exception as e:
|
||||
ip = get_ipython()
|
||||
if ip is None:
|
||||
self.log.warning("Exception in callback %s: %s", callback, e, exc_info=True)
|
||||
else:
|
||||
ip.showtraceback()
|
||||
else:
|
||||
value = local_value if local_value is not None else value
|
||||
return value
|
||||
|
||||
def register_callback(self, callback, remove=False):
|
||||
"""(Un)Register a callback
|
||||
|
||||
Parameters
|
||||
----------
|
||||
callback: method handle
|
||||
Method to be registered or unregistered.
|
||||
remove=False: bool
|
||||
Whether to unregister the callback."""
|
||||
|
||||
# (Un)Register the callback.
|
||||
if remove and callback in self.callbacks:
|
||||
self.callbacks.remove(callback)
|
||||
elif not remove and callback not in self.callbacks:
|
||||
self.callbacks.append(callback)
|
||||
|
||||
def _show_traceback(method):
|
||||
"""decorator for showing tracebacks in IPython"""
|
||||
def m(self, *args, **kwargs):
|
||||
try:
|
||||
return(method(self, *args, **kwargs))
|
||||
except Exception as e:
|
||||
ip = get_ipython()
|
||||
if ip is None:
|
||||
self.log.warning("Exception in widget method %s: %s", method, e, exc_info=True)
|
||||
else:
|
||||
ip.showtraceback()
|
||||
return m
|
||||
|
||||
|
||||
class WidgetRegistry(object):
|
||||
|
||||
def __init__(self):
|
||||
self._registry = {}
|
||||
|
||||
def register(self, model_module, model_module_version_range, model_name, view_module, view_module_version_range, view_name, klass):
|
||||
"""Register a value"""
|
||||
model_module = self._registry.setdefault(model_module, {})
|
||||
model_version = model_module.setdefault(model_module_version_range, {})
|
||||
model_name = model_version.setdefault(model_name, {})
|
||||
view_module = model_name.setdefault(view_module, {})
|
||||
view_version = view_module.setdefault(view_module_version_range, {})
|
||||
view_version[view_name] = klass
|
||||
|
||||
def get(self, model_module, model_module_version, model_name, view_module, view_module_version, view_name):
|
||||
"""Get a value"""
|
||||
module_versions = self._registry[model_module]
|
||||
# The python semver module doesn't work well, for example, it can't do match('3', '*')
|
||||
# so we just take the first model module version.
|
||||
#model_names = next(v for k, v in module_versions.items()
|
||||
# if semver.match(model_module_version, k))
|
||||
model_names = list(module_versions.values())[0]
|
||||
view_modules = model_names[model_name]
|
||||
view_versions = view_modules[view_module]
|
||||
# The python semver module doesn't work well, so we just take the first view module version
|
||||
#view_names = next(v for k, v in view_versions.items()
|
||||
# if semver.match(view_module_version, k))
|
||||
view_names = list(view_versions.values())[0]
|
||||
widget_class = view_names[view_name]
|
||||
return widget_class
|
||||
|
||||
def items(self):
|
||||
for model_module, mm in sorted(self._registry.items()):
|
||||
for model_version, mv in sorted(mm.items()):
|
||||
for model_name, vm in sorted(mv.items()):
|
||||
for view_module, vv in sorted(vm.items()):
|
||||
for view_version, vn in sorted(vv.items()):
|
||||
for view_name, widget in sorted(vn.items()):
|
||||
yield (model_module, model_version, model_name, view_module, view_version, view_name), widget
|
||||
|
||||
def register(name=''):
|
||||
"For backwards compatibility, we support @register(name) syntax."
|
||||
def reg(widget):
|
||||
"""A decorator registering a widget class in the widget registry."""
|
||||
w = widget.class_traits()
|
||||
Widget.widget_types.register(w['_model_module'].default_value,
|
||||
w['_model_module_version'].default_value,
|
||||
w['_model_name'].default_value,
|
||||
w['_view_module'].default_value,
|
||||
w['_view_module_version'].default_value,
|
||||
w['_view_name'].default_value,
|
||||
widget)
|
||||
return widget
|
||||
if isinstance(name, string_types):
|
||||
import warnings
|
||||
warnings.warn("Widget registration using a string name has been deprecated. Widget registration now uses a plain `@register` decorator.", DeprecationWarning)
|
||||
return reg
|
||||
else:
|
||||
return reg(name)
|
||||
|
||||
|
||||
class Widget(LoggingHasTraits):
|
||||
#-------------------------------------------------------------------------
|
||||
# Class attributes
|
||||
#-------------------------------------------------------------------------
|
||||
_widget_construction_callback = None
|
||||
|
||||
# widgets is a dictionary of all active widget objects
|
||||
widgets = {}
|
||||
|
||||
# widget_types is a registry of widgets by module, version, and name:
|
||||
widget_types = WidgetRegistry()
|
||||
|
||||
@classmethod
|
||||
def close_all(cls):
|
||||
for widget in list(cls.widgets.values()):
|
||||
widget.close()
|
||||
|
||||
|
||||
@staticmethod
|
||||
def on_widget_constructed(callback):
|
||||
"""Registers a callback to be called when a widget is constructed.
|
||||
|
||||
The callback must have the following signature:
|
||||
callback(widget)"""
|
||||
Widget._widget_construction_callback = callback
|
||||
|
||||
@staticmethod
|
||||
def _call_widget_constructed(widget):
|
||||
"""Static method, called when a widget is constructed."""
|
||||
if Widget._widget_construction_callback is not None and callable(Widget._widget_construction_callback):
|
||||
Widget._widget_construction_callback(widget)
|
||||
|
||||
@staticmethod
|
||||
def handle_comm_opened(comm, msg):
|
||||
"""Static method, called when a widget is constructed."""
|
||||
version = msg.get('metadata', {}).get('version', '')
|
||||
if version.split('.')[0] != PROTOCOL_VERSION_MAJOR:
|
||||
raise ValueError("Incompatible widget protocol versions: received version %r, expected version %r"%(version, __protocol_version__))
|
||||
data = msg['content']['data']
|
||||
state = data['state']
|
||||
|
||||
# Find the widget class to instantiate in the registered widgets
|
||||
widget_class = Widget.widget_types.get(state['_model_module'],
|
||||
state['_model_module_version'],
|
||||
state['_model_name'],
|
||||
state['_view_module'],
|
||||
state['_view_module_version'],
|
||||
state['_view_name'])
|
||||
widget = widget_class(comm=comm)
|
||||
if 'buffer_paths' in data:
|
||||
_put_buffers(state, data['buffer_paths'], msg['buffers'])
|
||||
widget.set_state(state)
|
||||
|
||||
@staticmethod
|
||||
def get_manager_state(drop_defaults=False, widgets=None):
|
||||
"""Returns the full state for a widget manager for embedding
|
||||
|
||||
:param drop_defaults: when True, it will not include default value
|
||||
:param widgets: list with widgets to include in the state (or all widgets when None)
|
||||
:return:
|
||||
"""
|
||||
state = {}
|
||||
if widgets is None:
|
||||
widgets = Widget.widgets.values()
|
||||
for widget in widgets:
|
||||
state[widget.model_id] = widget._get_embed_state(drop_defaults=drop_defaults)
|
||||
return {'version_major': 2, 'version_minor': 0, 'state': state}
|
||||
|
||||
def _get_embed_state(self, drop_defaults=False):
|
||||
state = {
|
||||
'model_name': self._model_name,
|
||||
'model_module': self._model_module,
|
||||
'model_module_version': self._model_module_version
|
||||
}
|
||||
model_state, buffer_paths, buffers = _remove_buffers(self.get_state(drop_defaults=drop_defaults))
|
||||
state['state'] = model_state
|
||||
if len(buffers) > 0:
|
||||
state['buffers'] = [{'encoding': 'base64',
|
||||
'path': p,
|
||||
'data': standard_b64encode(d).decode('ascii')}
|
||||
for p, d in zip(buffer_paths, buffers)]
|
||||
return state
|
||||
|
||||
def get_view_spec(self):
|
||||
return dict(version_major=2, version_minor=0, model_id=self._model_id)
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Traits
|
||||
#-------------------------------------------------------------------------
|
||||
_model_name = Unicode('WidgetModel',
|
||||
help="Name of the model.", read_only=True).tag(sync=True)
|
||||
_model_module = Unicode('@jupyter-widgets/base',
|
||||
help="The namespace for the model.", read_only=True).tag(sync=True)
|
||||
_model_module_version = Unicode(__jupyter_widgets_base_version__,
|
||||
help="A semver requirement for namespace version containing the model.", read_only=True).tag(sync=True)
|
||||
_view_name = Unicode(None, allow_none=True,
|
||||
help="Name of the view.").tag(sync=True)
|
||||
_view_module = Unicode(None, allow_none=True,
|
||||
help="The namespace for the view.").tag(sync=True)
|
||||
_view_module_version = Unicode('',
|
||||
help="A semver requirement for the namespace version containing the view.").tag(sync=True)
|
||||
|
||||
_view_count = Int(None, allow_none=True,
|
||||
help="EXPERIMENTAL: The number of views of the model displayed in the frontend. This attribute is experimental and may change or be removed in the future. None signifies that views will not be tracked. Set this to 0 to start tracking view creation/deletion.").tag(sync=True)
|
||||
comm = Instance('ipykernel.comm.Comm', allow_none=True)
|
||||
|
||||
keys = List(help="The traits which are synced.")
|
||||
|
||||
@default('keys')
|
||||
def _default_keys(self):
|
||||
return [name for name in self.traits(sync=True)]
|
||||
|
||||
_property_lock = Dict()
|
||||
_holding_sync = False
|
||||
_states_to_send = Set()
|
||||
_display_callbacks = Instance(CallbackDispatcher, ())
|
||||
_msg_callbacks = Instance(CallbackDispatcher, ())
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# (Con/de)structor
|
||||
#-------------------------------------------------------------------------
|
||||
def __init__(self, **kwargs):
|
||||
"""Public constructor"""
|
||||
self._model_id = kwargs.pop('model_id', None)
|
||||
super(Widget, self).__init__(**kwargs)
|
||||
|
||||
Widget._call_widget_constructed(self)
|
||||
self.open()
|
||||
|
||||
def __del__(self):
|
||||
"""Object disposal"""
|
||||
self.close()
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Properties
|
||||
#-------------------------------------------------------------------------
|
||||
|
||||
def open(self):
|
||||
"""Open a comm to the frontend if one isn't already open."""
|
||||
if self.comm is None:
|
||||
state, buffer_paths, buffers = _remove_buffers(self.get_state())
|
||||
|
||||
args = dict(target_name='jupyter.widget',
|
||||
data={'state': state, 'buffer_paths': buffer_paths},
|
||||
buffers=buffers,
|
||||
metadata={'version': __protocol_version__}
|
||||
)
|
||||
if self._model_id is not None:
|
||||
args['comm_id'] = self._model_id
|
||||
|
||||
self.comm = Comm(**args)
|
||||
|
||||
@observe('comm')
|
||||
def _comm_changed(self, change):
|
||||
"""Called when the comm is changed."""
|
||||
if change['new'] is None:
|
||||
return
|
||||
self._model_id = self.model_id
|
||||
|
||||
self.comm.on_msg(self._handle_msg)
|
||||
Widget.widgets[self.model_id] = self
|
||||
|
||||
@property
|
||||
def model_id(self):
|
||||
"""Gets the model id of this widget.
|
||||
|
||||
If a Comm doesn't exist yet, a Comm will be created automagically."""
|
||||
return self.comm.comm_id
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Methods
|
||||
#-------------------------------------------------------------------------
|
||||
|
||||
def close(self):
|
||||
"""Close method.
|
||||
|
||||
Closes the underlying comm.
|
||||
When the comm is closed, all of the widget views are automatically
|
||||
removed from the front-end."""
|
||||
if self.comm is not None:
|
||||
Widget.widgets.pop(self.model_id, None)
|
||||
self.comm.close()
|
||||
self.comm = None
|
||||
self._ipython_display_ = None
|
||||
|
||||
def send_state(self, key=None):
|
||||
"""Sends the widget state, or a piece of it, to the front-end, if it exists.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
key : unicode, or iterable (optional)
|
||||
A single property's name or iterable of property names to sync with the front-end.
|
||||
"""
|
||||
state = self.get_state(key=key)
|
||||
if len(state) > 0:
|
||||
if self._property_lock: # we need to keep this dict up to date with the front-end values
|
||||
for name, value in state.items():
|
||||
if name in self._property_lock:
|
||||
self._property_lock[name] = value
|
||||
state, buffer_paths, buffers = _remove_buffers(state)
|
||||
msg = {'method': 'update', 'state': state, 'buffer_paths': buffer_paths}
|
||||
self._send(msg, buffers=buffers)
|
||||
|
||||
|
||||
def get_state(self, key=None, drop_defaults=False):
|
||||
"""Gets the widget state, or a piece of it.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
key : unicode or iterable (optional)
|
||||
A single property's name or iterable of property names to get.
|
||||
|
||||
Returns
|
||||
-------
|
||||
state : dict of states
|
||||
metadata : dict
|
||||
metadata for each field: {key: metadata}
|
||||
"""
|
||||
if key is None:
|
||||
keys = self.keys
|
||||
elif isinstance(key, string_types):
|
||||
keys = [key]
|
||||
elif isinstance(key, Iterable):
|
||||
keys = key
|
||||
else:
|
||||
raise ValueError("key must be a string, an iterable of keys, or None")
|
||||
state = {}
|
||||
traits = self.traits()
|
||||
for k in keys:
|
||||
to_json = self.trait_metadata(k, 'to_json', self._trait_to_json)
|
||||
value = to_json(getattr(self, k), self)
|
||||
if not PY3 and isinstance(traits[k], Bytes) and isinstance(value, bytes):
|
||||
value = memoryview(value)
|
||||
if not drop_defaults or not self._compare(value, traits[k].default_value):
|
||||
state[k] = value
|
||||
return state
|
||||
|
||||
def _is_numpy(self, x):
|
||||
return x.__class__.__name__ == 'ndarray' and x.__class__.__module__ == 'numpy'
|
||||
|
||||
def _compare(self, a, b):
|
||||
if self._is_numpy(a) or self._is_numpy(b):
|
||||
import numpy as np
|
||||
return np.array_equal(a, b)
|
||||
else:
|
||||
return a == b
|
||||
|
||||
def set_state(self, sync_data):
|
||||
"""Called when a state is received from the front-end."""
|
||||
# The order of these context managers is important. Properties must
|
||||
# be locked when the hold_trait_notification context manager is
|
||||
# released and notifications are fired.
|
||||
with self._lock_property(**sync_data), self.hold_trait_notifications():
|
||||
for name in sync_data:
|
||||
if name in self.keys:
|
||||
from_json = self.trait_metadata(name, 'from_json',
|
||||
self._trait_from_json)
|
||||
self.set_trait(name, from_json(sync_data[name], self))
|
||||
|
||||
def send(self, content, buffers=None):
|
||||
"""Sends a custom msg to the widget model in the front-end.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
content : dict
|
||||
Content of the message to send.
|
||||
buffers : list of binary buffers
|
||||
Binary buffers to send with message
|
||||
"""
|
||||
self._send({"method": "custom", "content": content}, buffers=buffers)
|
||||
|
||||
def on_msg(self, callback, remove=False):
|
||||
"""(Un)Register a custom msg receive callback.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
callback: callable
|
||||
callback will be passed three arguments when a message arrives::
|
||||
|
||||
callback(widget, content, buffers)
|
||||
|
||||
remove: bool
|
||||
True if the callback should be unregistered."""
|
||||
self._msg_callbacks.register_callback(callback, remove=remove)
|
||||
|
||||
def on_displayed(self, callback, remove=False):
|
||||
"""(Un)Register a widget displayed callback.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
callback: method handler
|
||||
Must have a signature of::
|
||||
|
||||
callback(widget, **kwargs)
|
||||
|
||||
kwargs from display are passed through without modification.
|
||||
remove: bool
|
||||
True if the callback should be unregistered."""
|
||||
self._display_callbacks.register_callback(callback, remove=remove)
|
||||
|
||||
def add_traits(self, **traits):
|
||||
"""Dynamically add trait attributes to the Widget."""
|
||||
super(Widget, self).add_traits(**traits)
|
||||
for name, trait in traits.items():
|
||||
if trait.get_metadata('sync'):
|
||||
self.keys.append(name)
|
||||
self.send_state(name)
|
||||
|
||||
def notify_change(self, change):
|
||||
"""Called when a property has changed."""
|
||||
# Send the state to the frontend before the user-registered callbacks
|
||||
# are called.
|
||||
name = change['name']
|
||||
if self.comm is not None and self.comm.kernel is not None:
|
||||
# Make sure this isn't information that the front-end just sent us.
|
||||
if name in self.keys and self._should_send_property(name, getattr(self, name)):
|
||||
# Send new state to front-end
|
||||
self.send_state(key=name)
|
||||
super(Widget, self).notify_change(change)
|
||||
|
||||
def __repr__(self):
|
||||
return self._gen_repr_from_keys(self._repr_keys())
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Support methods
|
||||
#-------------------------------------------------------------------------
|
||||
|
||||
@contextmanager
|
||||
def _lock_property(self, **properties):
|
||||
"""Lock a property-value pair.
|
||||
|
||||
The value should be the JSON state of the property.
|
||||
|
||||
NOTE: This, in addition to the single lock for all state changes, is
|
||||
flawed. In the future we may want to look into buffering state changes
|
||||
back to the front-end."""
|
||||
self._property_lock = properties
|
||||
try:
|
||||
yield
|
||||
finally:
|
||||
self._property_lock = {}
|
||||
|
||||
@contextmanager
|
||||
def hold_sync(self):
|
||||
"""Hold syncing any state until the outermost context manager exits"""
|
||||
if self._holding_sync is True:
|
||||
yield
|
||||
else:
|
||||
try:
|
||||
self._holding_sync = True
|
||||
yield
|
||||
finally:
|
||||
self._holding_sync = False
|
||||
self.send_state(self._states_to_send)
|
||||
self._states_to_send.clear()
|
||||
|
||||
def _should_send_property(self, key, value):
|
||||
"""Check the property lock (property_lock)"""
|
||||
to_json = self.trait_metadata(key, 'to_json', self._trait_to_json)
|
||||
if key in self._property_lock:
|
||||
# model_state, buffer_paths, buffers
|
||||
split_value = _remove_buffers({ key: to_json(value, self)})
|
||||
split_lock = _remove_buffers({ key: self._property_lock[key]})
|
||||
# A roundtrip conversion through json in the comparison takes care of
|
||||
# idiosyncracies of how python data structures map to json, for example
|
||||
# tuples get converted to lists.
|
||||
if (jsonloads(jsondumps(split_value[0])) == split_lock[0]
|
||||
and split_value[1] == split_lock[1]
|
||||
and _buffer_list_equal(split_value[2], split_lock[2])):
|
||||
return False
|
||||
if self._holding_sync:
|
||||
self._states_to_send.add(key)
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
# Event handlers
|
||||
@_show_traceback
|
||||
def _handle_msg(self, msg):
|
||||
"""Called when a msg is received from the front-end"""
|
||||
data = msg['content']['data']
|
||||
method = data['method']
|
||||
|
||||
if method == 'update':
|
||||
if 'state' in data:
|
||||
state = data['state']
|
||||
if 'buffer_paths' in data:
|
||||
_put_buffers(state, data['buffer_paths'], msg['buffers'])
|
||||
self.set_state(state)
|
||||
|
||||
# Handle a state request.
|
||||
elif method == 'request_state':
|
||||
self.send_state()
|
||||
|
||||
# Handle a custom msg from the front-end.
|
||||
elif method == 'custom':
|
||||
if 'content' in data:
|
||||
self._handle_custom_msg(data['content'], msg['buffers'])
|
||||
|
||||
# Catch remainder.
|
||||
else:
|
||||
self.log.error('Unknown front-end to back-end widget msg with method "%s"' % method)
|
||||
|
||||
def _handle_custom_msg(self, content, buffers):
|
||||
"""Called when a custom msg is received."""
|
||||
self._msg_callbacks(self, content, buffers)
|
||||
|
||||
def _handle_displayed(self, **kwargs):
|
||||
"""Called when a view has been displayed for this widget instance"""
|
||||
self._display_callbacks(self, **kwargs)
|
||||
|
||||
@staticmethod
|
||||
def _trait_to_json(x, self):
|
||||
"""Convert a trait value to json."""
|
||||
return x
|
||||
|
||||
@staticmethod
|
||||
def _trait_from_json(x, self):
|
||||
"""Convert json values to objects."""
|
||||
return x
|
||||
|
||||
def _ipython_display_(self, **kwargs):
|
||||
"""Called when `IPython.display.display` is called on the widget."""
|
||||
|
||||
plaintext = repr(self)
|
||||
if len(plaintext) > 110:
|
||||
plaintext = plaintext[:110] + '…'
|
||||
data = {
|
||||
'text/plain': plaintext,
|
||||
}
|
||||
if self._view_name is not None:
|
||||
# The 'application/vnd.jupyter.widget-view+json' mimetype has not been registered yet.
|
||||
# See the registration process and naming convention at
|
||||
# http://tools.ietf.org/html/rfc6838
|
||||
# and the currently registered mimetypes at
|
||||
# http://www.iana.org/assignments/media-types/media-types.xhtml.
|
||||
data['application/vnd.jupyter.widget-view+json'] = {
|
||||
'version_major': 2,
|
||||
'version_minor': 0,
|
||||
'model_id': self._model_id
|
||||
}
|
||||
display(data, raw=True)
|
||||
|
||||
if self._view_name is not None:
|
||||
self._handle_displayed(**kwargs)
|
||||
|
||||
def _send(self, msg, buffers=None):
|
||||
"""Sends a message to the model in the front-end."""
|
||||
if self.comm is not None and self.comm.kernel is not None:
|
||||
self.comm.send(data=msg, buffers=buffers)
|
||||
|
||||
def _repr_keys(self):
|
||||
traits = self.traits()
|
||||
for key in sorted(self.keys):
|
||||
# Exclude traits that start with an underscore
|
||||
if key[0] == '_':
|
||||
continue
|
||||
# Exclude traits who are equal to their default value
|
||||
value = getattr(self, key)
|
||||
trait = traits[key]
|
||||
if self._compare(value, trait.default_value):
|
||||
continue
|
||||
elif (isinstance(trait, (Container, Dict)) and
|
||||
trait.default_value == Undefined and
|
||||
(value is None or len(value) == 0)):
|
||||
# Empty container, and dynamic default will be empty
|
||||
continue
|
||||
yield key
|
||||
|
||||
def _gen_repr_from_keys(self, keys):
|
||||
class_name = self.__class__.__name__
|
||||
signature = ', '.join(
|
||||
'%s=%r' % (key, getattr(self, key))
|
||||
for key in keys
|
||||
)
|
||||
return '%s(%s)' % (class_name, signature)
|
85
venv/Lib/site-packages/ipywidgets/widgets/widget_bool.py
Normal file
85
venv/Lib/site-packages/ipywidgets/widgets/widget_bool.py
Normal file
|
@ -0,0 +1,85 @@
|
|||
# Copyright (c) Jupyter Development Team.
|
||||
# Distributed under the terms of the Modified BSD License.
|
||||
|
||||
"""Bool class.
|
||||
|
||||
Represents a boolean using a widget.
|
||||
"""
|
||||
|
||||
from .widget_description import DescriptionWidget
|
||||
from .widget_core import CoreWidget
|
||||
from .valuewidget import ValueWidget
|
||||
from .widget import register
|
||||
from traitlets import Unicode, Bool, CaselessStrEnum
|
||||
|
||||
|
||||
class _Bool(DescriptionWidget, ValueWidget, CoreWidget):
|
||||
"""A base class for creating widgets that represent booleans."""
|
||||
value = Bool(False, help="Bool value").tag(sync=True)
|
||||
disabled = Bool(False, help="Enable or disable user changes.").tag(sync=True)
|
||||
|
||||
def __init__(self, value=None, **kwargs):
|
||||
if value is not None:
|
||||
kwargs['value'] = value
|
||||
super(_Bool, self).__init__(**kwargs)
|
||||
|
||||
_model_name = Unicode('BoolModel').tag(sync=True)
|
||||
|
||||
|
||||
@register
|
||||
class Checkbox(_Bool):
|
||||
"""Displays a boolean `value` in the form of a checkbox.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
value : {True,False}
|
||||
value of the checkbox: True-checked, False-unchecked
|
||||
description : str
|
||||
description displayed next to the checkbox
|
||||
indent : {True,False}
|
||||
indent the control to align with other controls with a description. The style.description_width attribute controls this width for consistence with other controls.
|
||||
"""
|
||||
_view_name = Unicode('CheckboxView').tag(sync=True)
|
||||
_model_name = Unicode('CheckboxModel').tag(sync=True)
|
||||
indent = Bool(True, help="Indent the control to align with other controls with a description.").tag(sync=True)
|
||||
|
||||
|
||||
@register
|
||||
class ToggleButton(_Bool):
|
||||
"""Displays a boolean `value` in the form of a toggle button.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
value : {True,False}
|
||||
value of the toggle button: True-pressed, False-unpressed
|
||||
description : str
|
||||
description displayed next to the button
|
||||
tooltip: str
|
||||
tooltip caption of the toggle button
|
||||
icon: str
|
||||
font-awesome icon name
|
||||
"""
|
||||
_view_name = Unicode('ToggleButtonView').tag(sync=True)
|
||||
_model_name = Unicode('ToggleButtonModel').tag(sync=True)
|
||||
|
||||
tooltip = Unicode(help="Tooltip caption of the toggle button.").tag(sync=True)
|
||||
icon = Unicode('', help= "Font-awesome icon.").tag(sync=True)
|
||||
|
||||
button_style = CaselessStrEnum(
|
||||
values=['primary', 'success', 'info', 'warning', 'danger', ''], default_value='',
|
||||
help="""Use a predefined styling for the button.""").tag(sync=True)
|
||||
|
||||
|
||||
@register
|
||||
class Valid(_Bool):
|
||||
"""Displays a boolean `value` in the form of a green check (True / valid)
|
||||
or a red cross (False / invalid).
|
||||
|
||||
Parameters
|
||||
----------
|
||||
value: {True,False}
|
||||
value of the Valid widget
|
||||
"""
|
||||
readout = Unicode('Invalid', help="Message displayed when the value is False").tag(sync=True)
|
||||
_view_name = Unicode('ValidView').tag(sync=True)
|
||||
_model_name = Unicode('ValidModel').tag(sync=True)
|
132
venv/Lib/site-packages/ipywidgets/widgets/widget_box.py
Normal file
132
venv/Lib/site-packages/ipywidgets/widgets/widget_box.py
Normal file
|
@ -0,0 +1,132 @@
|
|||
# Copyright (c) Jupyter Development Team.
|
||||
# Distributed under the terms of the Modified BSD License.
|
||||
|
||||
"""Box widgets.
|
||||
|
||||
These widgets are containers that can be used to
|
||||
group other widgets together and control their
|
||||
relative layouts.
|
||||
"""
|
||||
|
||||
from .widget import register, widget_serialization, Widget
|
||||
from .domwidget import DOMWidget
|
||||
from .widget_core import CoreWidget
|
||||
from .docutils import doc_subst
|
||||
from .trait_types import TypedTuple
|
||||
from traitlets import Unicode, CaselessStrEnum, Instance
|
||||
|
||||
|
||||
_doc_snippets = {}
|
||||
_doc_snippets['box_params'] = """
|
||||
children: iterable of Widget instances
|
||||
list of widgets to display
|
||||
|
||||
box_style: str
|
||||
one of 'success', 'info', 'warning' or 'danger', or ''.
|
||||
Applies a predefined style to the box. Defaults to '',
|
||||
which applies no pre-defined style.
|
||||
"""
|
||||
|
||||
|
||||
@register
|
||||
@doc_subst(_doc_snippets)
|
||||
class Box(DOMWidget, CoreWidget):
|
||||
""" Displays multiple widgets in a group.
|
||||
|
||||
The widgets are laid out horizontally.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
{box_params}
|
||||
|
||||
Examples
|
||||
--------
|
||||
>>> import ipywidgets as widgets
|
||||
>>> title_widget = widgets.HTML('<em>Box Example</em>')
|
||||
>>> slider = widgets.IntSlider()
|
||||
>>> widgets.Box([title_widget, slider])
|
||||
"""
|
||||
_model_name = Unicode('BoxModel').tag(sync=True)
|
||||
_view_name = Unicode('BoxView').tag(sync=True)
|
||||
|
||||
# Child widgets in the container.
|
||||
# Using a tuple here to force reassignment to update the list.
|
||||
# When a proper notifying-list trait exists, use that instead.
|
||||
children = TypedTuple(trait=Instance(Widget), help="List of widget children").tag(
|
||||
sync=True, **widget_serialization)
|
||||
|
||||
box_style = CaselessStrEnum(
|
||||
values=['success', 'info', 'warning', 'danger', ''], default_value='',
|
||||
help="""Use a predefined styling for the box.""").tag(sync=True)
|
||||
|
||||
def __init__(self, children=(), **kwargs):
|
||||
kwargs['children'] = children
|
||||
super(Box, self).__init__(**kwargs)
|
||||
self.on_displayed(Box._fire_children_displayed)
|
||||
|
||||
def _fire_children_displayed(self):
|
||||
for child in self.children:
|
||||
child._handle_displayed()
|
||||
|
||||
|
||||
@register
|
||||
@doc_subst(_doc_snippets)
|
||||
class VBox(Box):
|
||||
""" Displays multiple widgets vertically using the flexible box model.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
{box_params}
|
||||
|
||||
Examples
|
||||
--------
|
||||
>>> import ipywidgets as widgets
|
||||
>>> title_widget = widgets.HTML('<em>Vertical Box Example</em>')
|
||||
>>> slider = widgets.IntSlider()
|
||||
>>> widgets.VBox([title_widget, slider])
|
||||
"""
|
||||
_model_name = Unicode('VBoxModel').tag(sync=True)
|
||||
_view_name = Unicode('VBoxView').tag(sync=True)
|
||||
|
||||
|
||||
@register
|
||||
@doc_subst(_doc_snippets)
|
||||
class HBox(Box):
|
||||
""" Displays multiple widgets horizontally using the flexible box model.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
{box_params}
|
||||
|
||||
Examples
|
||||
--------
|
||||
>>> import ipywidgets as widgets
|
||||
>>> title_widget = widgets.HTML('<em>Horizontal Box Example</em>')
|
||||
>>> slider = widgets.IntSlider()
|
||||
>>> widgets.HBox([title_widget, slider])
|
||||
"""
|
||||
_model_name = Unicode('HBoxModel').tag(sync=True)
|
||||
_view_name = Unicode('HBoxView').tag(sync=True)
|
||||
|
||||
|
||||
@register
|
||||
class GridBox(Box):
|
||||
""" Displays multiple widgets in rows and columns using the grid box model.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
{box_params}
|
||||
|
||||
Examples
|
||||
--------
|
||||
>>> import ipywidgets as widgets
|
||||
>>> title_widget = widgets.HTML('<em>Grid Box Example</em>')
|
||||
>>> slider = widgets.IntSlider()
|
||||
>>> button1 = widgets.Button(description='1')
|
||||
>>> button2 = widgets.Button(description='2')
|
||||
>>> # Create a grid with two columns, splitting space equally
|
||||
>>> layout = widgets.Layout(grid_template_columns='1fr 1fr')
|
||||
>>> widgets.GridBox([title_widget, slider, button1, button2], layout=layout)
|
||||
"""
|
||||
_model_name = Unicode('GridBoxModel').tag(sync=True)
|
||||
_view_name = Unicode('GridBoxView').tag(sync=True)
|
105
venv/Lib/site-packages/ipywidgets/widgets/widget_button.py
Normal file
105
venv/Lib/site-packages/ipywidgets/widgets/widget_button.py
Normal file
|
@ -0,0 +1,105 @@
|
|||
# Copyright (c) Jupyter Development Team.
|
||||
# Distributed under the terms of the Modified BSD License.
|
||||
|
||||
"""Button class.
|
||||
|
||||
Represents a button in the frontend using a widget. Allows user to listen for
|
||||
click events on the button and trigger backend code when the clicks are fired.
|
||||
"""
|
||||
|
||||
from .domwidget import DOMWidget
|
||||
from .widget import CallbackDispatcher, register, widget_serialization
|
||||
from .widget_core import CoreWidget
|
||||
from .widget_style import Style
|
||||
from .trait_types import Color, InstanceDict
|
||||
|
||||
from traitlets import Unicode, Bool, CaselessStrEnum, Instance, validate, default
|
||||
import warnings
|
||||
|
||||
|
||||
@register
|
||||
class ButtonStyle(Style, CoreWidget):
|
||||
"""Button style widget."""
|
||||
_model_name = Unicode('ButtonStyleModel').tag(sync=True)
|
||||
button_color = Color(None, allow_none=True, help="Color of the button").tag(sync=True)
|
||||
font_weight = Unicode(help="Button text font weight.").tag(sync=True)
|
||||
|
||||
|
||||
@register
|
||||
class Button(DOMWidget, CoreWidget):
|
||||
"""Button widget.
|
||||
|
||||
This widget has an `on_click` method that allows you to listen for the
|
||||
user clicking on the button. The click event itself is stateless.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
description: str
|
||||
description displayed next to the button
|
||||
tooltip: str
|
||||
tooltip caption of the toggle button
|
||||
icon: str
|
||||
font-awesome icon name
|
||||
disabled: bool
|
||||
whether user interaction is enabled
|
||||
"""
|
||||
_view_name = Unicode('ButtonView').tag(sync=True)
|
||||
_model_name = Unicode('ButtonModel').tag(sync=True)
|
||||
|
||||
description = Unicode(help="Button label.").tag(sync=True)
|
||||
tooltip = Unicode(help="Tooltip caption of the button.").tag(sync=True)
|
||||
disabled = Bool(False, help="Enable or disable user changes.").tag(sync=True)
|
||||
icon = Unicode('', help="Font-awesome icon name, without the 'fa-' prefix.").tag(sync=True)
|
||||
|
||||
button_style = CaselessStrEnum(
|
||||
values=['primary', 'success', 'info', 'warning', 'danger', ''], default_value='',
|
||||
help="""Use a predefined styling for the button.""").tag(sync=True)
|
||||
|
||||
style = InstanceDict(ButtonStyle).tag(sync=True, **widget_serialization)
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
super(Button, self).__init__(**kwargs)
|
||||
self._click_handlers = CallbackDispatcher()
|
||||
self.on_msg(self._handle_button_msg)
|
||||
|
||||
@validate('icon')
|
||||
def _validate_icon(self, proposal):
|
||||
"""Strip 'fa-' if necessary'"""
|
||||
value = proposal['value']
|
||||
if value.startswith('fa-'):
|
||||
warnings.warn("icons names no longer start with 'fa-', "
|
||||
"just use the class name itself (for example, 'check' instead of 'fa-check')", DeprecationWarning)
|
||||
value = value[3:]
|
||||
return value
|
||||
|
||||
def on_click(self, callback, remove=False):
|
||||
"""Register a callback to execute when the button is clicked.
|
||||
|
||||
The callback will be called with one argument, the clicked button
|
||||
widget instance.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
remove: bool (optional)
|
||||
Set to true to remove the callback from the list of callbacks.
|
||||
"""
|
||||
self._click_handlers.register_callback(callback, remove=remove)
|
||||
|
||||
def click(self):
|
||||
"""Programmatically trigger a click event.
|
||||
|
||||
This will call the callbacks registered to the clicked button
|
||||
widget instance.
|
||||
"""
|
||||
self._click_handlers(self)
|
||||
|
||||
def _handle_button_msg(self, _, content, buffers):
|
||||
"""Handle a msg from the front-end.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
content: dict
|
||||
Content of the msg.
|
||||
"""
|
||||
if content.get('event', '') == 'click':
|
||||
self.click()
|
24
venv/Lib/site-packages/ipywidgets/widgets/widget_color.py
Normal file
24
venv/Lib/site-packages/ipywidgets/widgets/widget_color.py
Normal file
|
@ -0,0 +1,24 @@
|
|||
# Copyright (c) Jupyter Development Team.
|
||||
# Distributed under the terms of the Modified BSD License.
|
||||
|
||||
"""Color class.
|
||||
|
||||
Represents an HTML Color .
|
||||
"""
|
||||
|
||||
from .widget_description import DescriptionWidget
|
||||
from .valuewidget import ValueWidget
|
||||
from .widget import register
|
||||
from .widget_core import CoreWidget
|
||||
from .trait_types import Color
|
||||
from traitlets import Unicode, Bool
|
||||
|
||||
|
||||
@register
|
||||
class ColorPicker(DescriptionWidget, ValueWidget, CoreWidget):
|
||||
value = Color('black', help="The color value.").tag(sync=True)
|
||||
concise = Bool(help="Display short version with just a color selector.").tag(sync=True)
|
||||
disabled = Bool(False, help="Enable or disable user changes.").tag(sync=True)
|
||||
|
||||
_view_name = Unicode('ColorPickerView').tag(sync=True)
|
||||
_model_name = Unicode('ColorPickerModel').tag(sync=True)
|
|
@ -0,0 +1,53 @@
|
|||
# Copyright (c) Jupyter Development Team.
|
||||
# Distributed under the terms of the Modified BSD License.
|
||||
|
||||
"""Controller class.
|
||||
|
||||
Represents a Gamepad or Joystick controller.
|
||||
"""
|
||||
|
||||
from .valuewidget import ValueWidget
|
||||
from .widget import register, widget_serialization
|
||||
from .domwidget import DOMWidget
|
||||
from .widget_core import CoreWidget
|
||||
from .trait_types import TypedTuple
|
||||
from traitlets import Bool, Int, Float, Unicode, Instance
|
||||
|
||||
|
||||
@register
|
||||
class Button(DOMWidget, ValueWidget, CoreWidget):
|
||||
"""Represents a gamepad or joystick button."""
|
||||
value = Float(min=0.0, max=1.0, read_only=True, help="The value of the button.").tag(sync=True)
|
||||
pressed = Bool(read_only=True, help="Whether the button is pressed.").tag(sync=True)
|
||||
|
||||
_view_name = Unicode('ControllerButtonView').tag(sync=True)
|
||||
_model_name = Unicode('ControllerButtonModel').tag(sync=True)
|
||||
|
||||
|
||||
@register
|
||||
class Axis(DOMWidget, ValueWidget, CoreWidget):
|
||||
"""Represents a gamepad or joystick axis."""
|
||||
value = Float(min=-1.0, max=1.0, read_only=True, help="The value of the axis.").tag(sync=True)
|
||||
|
||||
_view_name = Unicode('ControllerAxisView').tag(sync=True)
|
||||
_model_name = Unicode('ControllerAxisModel').tag(sync=True)
|
||||
|
||||
|
||||
@register
|
||||
class Controller(DOMWidget, CoreWidget):
|
||||
"""Represents a game controller."""
|
||||
index = Int(help="The id number of the controller.").tag(sync=True)
|
||||
|
||||
# General information about the gamepad, button and axes mapping, name.
|
||||
# These values are all read-only and set by the JavaScript side.
|
||||
name = Unicode(read_only=True, help="The name of the controller.").tag(sync=True)
|
||||
mapping = Unicode(read_only=True, help="The name of the control mapping.").tag(sync=True)
|
||||
connected = Bool(read_only=True, help="Whether the gamepad is connected.").tag(sync=True)
|
||||
timestamp = Float(read_only=True, help="The last time the data from this gamepad was updated.").tag(sync=True)
|
||||
|
||||
# Buttons and axes - read-only
|
||||
buttons = TypedTuple(trait=Instance(Button), read_only=True, help="The buttons on the gamepad.").tag(sync=True, **widget_serialization)
|
||||
axes = TypedTuple(trait=Instance(Axis), read_only=True, help="The axes on the gamepad.").tag(sync=True, **widget_serialization)
|
||||
|
||||
_view_name = Unicode('ControllerView').tag(sync=True)
|
||||
_model_name = Unicode('ControllerModel').tag(sync=True)
|
16
venv/Lib/site-packages/ipywidgets/widgets/widget_core.py
Normal file
16
venv/Lib/site-packages/ipywidgets/widgets/widget_core.py
Normal file
|
@ -0,0 +1,16 @@
|
|||
# Copyright (c) Jupyter Development Team.
|
||||
# Distributed under the terms of the Modified BSD License.
|
||||
|
||||
"""Base widget class for widgets provided in Core"""
|
||||
|
||||
from .widget import Widget
|
||||
from .._version import __jupyter_widgets_controls_version__
|
||||
|
||||
from traitlets import Unicode
|
||||
|
||||
class CoreWidget(Widget):
|
||||
|
||||
_model_module = Unicode('@jupyter-widgets/controls').tag(sync=True)
|
||||
_model_module_version = Unicode(__jupyter_widgets_controls_version__).tag(sync=True)
|
||||
_view_module = Unicode('@jupyter-widgets/controls').tag(sync=True)
|
||||
_view_module_version = Unicode(__jupyter_widgets_controls_version__).tag(sync=True)
|
44
venv/Lib/site-packages/ipywidgets/widgets/widget_date.py
Normal file
44
venv/Lib/site-packages/ipywidgets/widgets/widget_date.py
Normal file
|
@ -0,0 +1,44 @@
|
|||
# Copyright (c) Jupyter Development Team.
|
||||
# Distributed under the terms of the Modified BSD License.
|
||||
|
||||
"""Color class.
|
||||
|
||||
Represents an HTML Color .
|
||||
"""
|
||||
|
||||
from .widget_description import DescriptionWidget
|
||||
from .valuewidget import ValueWidget
|
||||
from .widget import register
|
||||
from .widget_core import CoreWidget
|
||||
from .trait_types import Date, date_serialization
|
||||
from traitlets import Unicode, Bool
|
||||
|
||||
|
||||
@register
|
||||
class DatePicker(DescriptionWidget, ValueWidget, CoreWidget):
|
||||
"""
|
||||
Display a widget for picking dates.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
|
||||
value: datetime.date
|
||||
The current value of the widget.
|
||||
|
||||
disabled: bool
|
||||
Whether to disable user changes.
|
||||
|
||||
Examples
|
||||
--------
|
||||
|
||||
>>> import datetime
|
||||
>>> import ipywidgets as widgets
|
||||
>>> date_pick = widgets.DatePicker()
|
||||
>>> date_pick.value = datetime.date(2019, 7, 9)
|
||||
"""
|
||||
value = Date(None, allow_none=True).tag(sync=True, **date_serialization)
|
||||
disabled = Bool(False, help="Enable or disable user changes.").tag(sync=True)
|
||||
|
||||
|
||||
_view_name = Unicode('DatePickerView').tag(sync=True)
|
||||
_model_name = Unicode('DatePickerModel').tag(sync=True)
|
|
@ -0,0 +1,34 @@
|
|||
# Copyright (c) Jupyter Development Team.
|
||||
# Distributed under the terms of the Modified BSD License.
|
||||
|
||||
"""Contains the DOMWidget class"""
|
||||
|
||||
from traitlets import Unicode
|
||||
from .widget import Widget, widget_serialization, register
|
||||
from .trait_types import InstanceDict
|
||||
from .widget_style import Style
|
||||
from .widget_core import CoreWidget
|
||||
from .domwidget import DOMWidget
|
||||
|
||||
@register
|
||||
class DescriptionStyle(Style, CoreWidget, Widget):
|
||||
"""Description style widget."""
|
||||
_model_name = Unicode('DescriptionStyleModel').tag(sync=True)
|
||||
description_width = Unicode(help="Width of the description to the side of the control.").tag(sync=True)
|
||||
|
||||
|
||||
class DescriptionWidget(DOMWidget, CoreWidget):
|
||||
"""Widget that has a description label to the side."""
|
||||
_model_name = Unicode('DescriptionModel').tag(sync=True)
|
||||
description = Unicode('', help="Description of the control.").tag(sync=True)
|
||||
description_tooltip = Unicode(None, allow_none=True, help="Tooltip for the description (defaults to description).").tag(sync=True)
|
||||
style = InstanceDict(DescriptionStyle, help="Styling customizations").tag(sync=True, **widget_serialization)
|
||||
|
||||
def _repr_keys(self):
|
||||
for key in super(DescriptionWidget, self)._repr_keys():
|
||||
# Exclude style if it had the default value
|
||||
if key == 'style':
|
||||
value = getattr(self, key)
|
||||
if repr(value) == '%s()' % value.__class__.__name__:
|
||||
continue
|
||||
yield key
|
354
venv/Lib/site-packages/ipywidgets/widgets/widget_float.py
Normal file
354
venv/Lib/site-packages/ipywidgets/widgets/widget_float.py
Normal file
|
@ -0,0 +1,354 @@
|
|||
"""Float class.
|
||||
|
||||
Represents an unbounded float using a widget.
|
||||
"""
|
||||
|
||||
# Copyright (c) Jupyter Development Team.
|
||||
# Distributed under the terms of the Modified BSD License.
|
||||
|
||||
from traitlets import (
|
||||
Instance, Unicode, CFloat, Bool, CaselessStrEnum, Tuple, TraitError, validate, default
|
||||
)
|
||||
from .widget_description import DescriptionWidget
|
||||
from .trait_types import InstanceDict, NumberFormat
|
||||
from .valuewidget import ValueWidget
|
||||
from .widget import register, widget_serialization
|
||||
from .widget_core import CoreWidget
|
||||
from .widget_int import ProgressStyle, SliderStyle
|
||||
|
||||
|
||||
class _Float(DescriptionWidget, ValueWidget, CoreWidget):
|
||||
value = CFloat(0.0, help="Float value").tag(sync=True)
|
||||
|
||||
def __init__(self, value=None, **kwargs):
|
||||
if value is not None:
|
||||
kwargs['value'] = value
|
||||
super(_Float, self).__init__(**kwargs)
|
||||
|
||||
|
||||
class _BoundedFloat(_Float):
|
||||
max = CFloat(100.0, help="Max value").tag(sync=True)
|
||||
min = CFloat(0.0, help="Min value").tag(sync=True)
|
||||
|
||||
@validate('value')
|
||||
def _validate_value(self, proposal):
|
||||
"""Cap and floor value"""
|
||||
value = proposal['value']
|
||||
if self.min > value or self.max < value:
|
||||
value = min(max(value, self.min), self.max)
|
||||
return value
|
||||
|
||||
@validate('min')
|
||||
def _validate_min(self, proposal):
|
||||
"""Enforce min <= value <= max"""
|
||||
min = proposal['value']
|
||||
if min > self.max:
|
||||
raise TraitError('Setting min > max')
|
||||
if min > self.value:
|
||||
self.value = min
|
||||
return min
|
||||
|
||||
@validate('max')
|
||||
def _validate_max(self, proposal):
|
||||
"""Enforce min <= value <= max"""
|
||||
max = proposal['value']
|
||||
if max < self.min:
|
||||
raise TraitError('setting max < min')
|
||||
if max < self.value:
|
||||
self.value = max
|
||||
return max
|
||||
|
||||
class _BoundedLogFloat(_Float):
|
||||
max = CFloat(4.0, help="Max value for the exponent").tag(sync=True)
|
||||
min = CFloat(0.0, help="Min value for the exponent").tag(sync=True)
|
||||
base = CFloat(10.0, help="Base of value").tag(sync=True)
|
||||
value = CFloat(1.0, help="Float value").tag(sync=True)
|
||||
|
||||
@validate('value')
|
||||
def _validate_value(self, proposal):
|
||||
"""Cap and floor value"""
|
||||
value = proposal['value']
|
||||
if self.base ** self.min > value or self.base ** self.max < value:
|
||||
value = min(max(value, self.base ** self.min), self.base ** self.max)
|
||||
return value
|
||||
|
||||
@validate('min')
|
||||
def _validate_min(self, proposal):
|
||||
"""Enforce base ** min <= value <= base ** max"""
|
||||
min = proposal['value']
|
||||
if min > self.max:
|
||||
raise TraitError('Setting min > max')
|
||||
if self.base ** min > self.value:
|
||||
self.value = self.base ** min
|
||||
return min
|
||||
|
||||
@validate('max')
|
||||
def _validate_max(self, proposal):
|
||||
"""Enforce base ** min <= value <= base ** max"""
|
||||
max = proposal['value']
|
||||
if max < self.min:
|
||||
raise TraitError('setting max < min')
|
||||
if self.base ** max < self.value:
|
||||
self.value = self.base ** max
|
||||
return max
|
||||
|
||||
|
||||
@register
|
||||
class FloatText(_Float):
|
||||
""" Displays a float value within a textbox. For a textbox in
|
||||
which the value must be within a specific range, use BoundedFloatText.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
value : float
|
||||
value displayed
|
||||
step : float
|
||||
step of the increment (if None, any step is allowed)
|
||||
description : str
|
||||
description displayed next to the text box
|
||||
"""
|
||||
_view_name = Unicode('FloatTextView').tag(sync=True)
|
||||
_model_name = Unicode('FloatTextModel').tag(sync=True)
|
||||
disabled = Bool(False, help="Enable or disable user changes").tag(sync=True)
|
||||
continuous_update = Bool(False, help="Update the value as the user types. If False, update on submission, e.g., pressing Enter or navigating away.").tag(sync=True)
|
||||
step = CFloat(None, allow_none=True, help="Minimum step to increment the value").tag(sync=True)
|
||||
|
||||
|
||||
@register
|
||||
class BoundedFloatText(_BoundedFloat):
|
||||
""" Displays a float value within a textbox. Value must be within the range specified.
|
||||
|
||||
For a textbox in which the value doesn't need to be within a specific range, use FloatText.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
value : float
|
||||
value displayed
|
||||
min : float
|
||||
minimal value of the range of possible values displayed
|
||||
max : float
|
||||
maximal value of the range of possible values displayed
|
||||
step : float
|
||||
step of the increment (if None, any step is allowed)
|
||||
description : str
|
||||
description displayed next to the textbox
|
||||
"""
|
||||
_view_name = Unicode('FloatTextView').tag(sync=True)
|
||||
_model_name = Unicode('BoundedFloatTextModel').tag(sync=True)
|
||||
disabled = Bool(False, help="Enable or disable user changes").tag(sync=True)
|
||||
continuous_update = Bool(False, help="Update the value as the user types. If False, update on submission, e.g., pressing Enter or navigating away.").tag(sync=True)
|
||||
step = CFloat(None, allow_none=True, help="Minimum step to increment the value").tag(sync=True)
|
||||
|
||||
@register
|
||||
class FloatSlider(_BoundedFloat):
|
||||
""" Slider/trackbar of floating values with the specified range.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
value : float
|
||||
position of the slider
|
||||
min : float
|
||||
minimal position of the slider
|
||||
max : float
|
||||
maximal position of the slider
|
||||
step : float
|
||||
step of the trackbar
|
||||
description : str
|
||||
name of the slider
|
||||
orientation : {'horizontal', 'vertical'}
|
||||
default is 'horizontal', orientation of the slider
|
||||
readout : {True, False}
|
||||
default is True, display the current value of the slider next to it
|
||||
readout_format : str
|
||||
default is '.2f', specifier for the format function used to represent
|
||||
slider value for human consumption, modeled after Python 3's format
|
||||
specification mini-language (PEP 3101).
|
||||
"""
|
||||
_view_name = Unicode('FloatSliderView').tag(sync=True)
|
||||
_model_name = Unicode('FloatSliderModel').tag(sync=True)
|
||||
step = CFloat(0.1, help="Minimum step to increment the value").tag(sync=True)
|
||||
orientation = CaselessStrEnum(values=['horizontal', 'vertical'],
|
||||
default_value='horizontal', help="Vertical or horizontal.").tag(sync=True)
|
||||
readout = Bool(True, help="Display the current value of the slider next to it.").tag(sync=True)
|
||||
readout_format = NumberFormat(
|
||||
'.2f', help="Format for the readout").tag(sync=True)
|
||||
continuous_update = Bool(True, help="Update the value of the widget as the user is holding the slider.").tag(sync=True)
|
||||
disabled = Bool(False, help="Enable or disable user changes").tag(sync=True)
|
||||
|
||||
style = InstanceDict(SliderStyle).tag(sync=True, **widget_serialization)
|
||||
|
||||
|
||||
@register
|
||||
class FloatLogSlider(_BoundedLogFloat):
|
||||
""" Slider/trackbar of logarithmic floating values with the specified range.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
value : float
|
||||
position of the slider
|
||||
base : float
|
||||
base of the logarithmic scale. Default is 10
|
||||
min : float
|
||||
minimal position of the slider in log scale, i.e., actual minimum is base ** min
|
||||
max : float
|
||||
maximal position of the slider in log scale, i.e., actual maximum is base ** max
|
||||
step : float
|
||||
step of the trackbar, denotes steps for the exponent, not the actual value
|
||||
description : str
|
||||
name of the slider
|
||||
orientation : {'horizontal', 'vertical'}
|
||||
default is 'horizontal', orientation of the slider
|
||||
readout : {True, False}
|
||||
default is True, display the current value of the slider next to it
|
||||
readout_format : str
|
||||
default is '.3g', specifier for the format function used to represent
|
||||
slider value for human consumption, modeled after Python 3's format
|
||||
specification mini-language (PEP 3101).
|
||||
"""
|
||||
_view_name = Unicode('FloatLogSliderView').tag(sync=True)
|
||||
_model_name = Unicode('FloatLogSliderModel').tag(sync=True)
|
||||
step = CFloat(0.1, help="Minimum step in the exponent to increment the value").tag(sync=True)
|
||||
orientation = CaselessStrEnum(values=['horizontal', 'vertical'],
|
||||
default_value='horizontal', help="Vertical or horizontal.").tag(sync=True)
|
||||
readout = Bool(True, help="Display the current value of the slider next to it.").tag(sync=True)
|
||||
readout_format = NumberFormat(
|
||||
'.3g', help="Format for the readout").tag(sync=True)
|
||||
continuous_update = Bool(True, help="Update the value of the widget as the user is holding the slider.").tag(sync=True)
|
||||
disabled = Bool(False, help="Enable or disable user changes").tag(sync=True)
|
||||
base = CFloat(10., help="Base for the logarithm").tag(sync=True)
|
||||
|
||||
style = InstanceDict(SliderStyle).tag(sync=True, **widget_serialization)
|
||||
|
||||
|
||||
@register
|
||||
class FloatProgress(_BoundedFloat):
|
||||
""" Displays a progress bar.
|
||||
|
||||
Parameters
|
||||
-----------
|
||||
value : float
|
||||
position within the range of the progress bar
|
||||
min : float
|
||||
minimal position of the slider
|
||||
max : float
|
||||
maximal position of the slider
|
||||
description : str
|
||||
name of the progress bar
|
||||
orientation : {'horizontal', 'vertical'}
|
||||
default is 'horizontal', orientation of the progress bar
|
||||
bar_style: {'success', 'info', 'warning', 'danger', ''}
|
||||
color of the progress bar, default is '' (blue)
|
||||
colors are: 'success'-green, 'info'-light blue, 'warning'-orange, 'danger'-red
|
||||
"""
|
||||
_view_name = Unicode('ProgressView').tag(sync=True)
|
||||
_model_name = Unicode('FloatProgressModel').tag(sync=True)
|
||||
orientation = CaselessStrEnum(values=['horizontal', 'vertical'],
|
||||
default_value='horizontal', help="Vertical or horizontal.").tag(sync=True)
|
||||
|
||||
bar_style = CaselessStrEnum(
|
||||
values=['success', 'info', 'warning', 'danger', ''],
|
||||
default_value='', allow_none=True,
|
||||
help="Use a predefined styling for the progess bar.").tag(sync=True)
|
||||
|
||||
style = InstanceDict(ProgressStyle).tag(sync=True, **widget_serialization)
|
||||
|
||||
|
||||
class _FloatRange(_Float):
|
||||
value = Tuple(CFloat(), CFloat(), default_value=(0.0, 1.0),
|
||||
help="Tuple of (lower, upper) bounds").tag(sync=True)
|
||||
|
||||
@property
|
||||
def lower(self):
|
||||
return self.value[0]
|
||||
|
||||
@lower.setter
|
||||
def lower(self, lower):
|
||||
self.value = (lower, self.value[1])
|
||||
|
||||
@property
|
||||
def upper(self):
|
||||
return self.value[1]
|
||||
|
||||
@upper.setter
|
||||
def upper(self, upper):
|
||||
self.value = (self.value[0], upper)
|
||||
|
||||
@validate('value')
|
||||
def _validate_value(self, proposal):
|
||||
lower, upper = proposal['value']
|
||||
if upper < lower:
|
||||
raise TraitError('setting lower > upper')
|
||||
return lower, upper
|
||||
|
||||
|
||||
class _BoundedFloatRange(_FloatRange):
|
||||
step = CFloat(1.0, help="Minimum step that the value can take (ignored by some views)").tag(sync=True)
|
||||
max = CFloat(100.0, help="Max value").tag(sync=True)
|
||||
min = CFloat(0.0, help="Min value").tag(sync=True)
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
min, max = kwargs.get('min', 0.0), kwargs.get('max', 100.0)
|
||||
if kwargs.get('value', None) is None:
|
||||
kwargs['value'] = (0.75 * min + 0.25 * max,
|
||||
0.25 * min + 0.75 * max)
|
||||
super(_BoundedFloatRange, self).__init__(*args, **kwargs)
|
||||
|
||||
@validate('min', 'max')
|
||||
def _validate_bounds(self, proposal):
|
||||
trait = proposal['trait']
|
||||
new = proposal['value']
|
||||
if trait.name == 'min' and new > self.max:
|
||||
raise TraitError('setting min > max')
|
||||
if trait.name == 'max' and new < self.min:
|
||||
raise TraitError('setting max < min')
|
||||
if trait.name == 'min':
|
||||
self.value = (max(new, self.value[0]), max(new, self.value[1]))
|
||||
if trait.name == 'max':
|
||||
self.value = (min(new, self.value[0]), min(new, self.value[1]))
|
||||
return new
|
||||
|
||||
@validate('value')
|
||||
def _validate_value(self, proposal):
|
||||
lower, upper = super(_BoundedFloatRange, self)._validate_value(proposal)
|
||||
lower, upper = min(lower, self.max), min(upper, self.max)
|
||||
lower, upper = max(lower, self.min), max(upper, self.min)
|
||||
return lower, upper
|
||||
|
||||
|
||||
@register
|
||||
class FloatRangeSlider(_BoundedFloatRange):
|
||||
""" Slider/trackbar that represents a pair of floats bounded by minimum and maximum value.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
value : float tuple
|
||||
range of the slider displayed
|
||||
min : float
|
||||
minimal position of the slider
|
||||
max : float
|
||||
maximal position of the slider
|
||||
step : float
|
||||
step of the trackbar
|
||||
description : str
|
||||
name of the slider
|
||||
orientation : {'horizontal', 'vertical'}
|
||||
default is 'horizontal'
|
||||
readout : {True, False}
|
||||
default is True, display the current value of the slider next to it
|
||||
readout_format : str
|
||||
default is '.2f', specifier for the format function used to represent
|
||||
slider value for human consumption, modeled after Python 3's format
|
||||
specification mini-language (PEP 3101).
|
||||
"""
|
||||
_view_name = Unicode('FloatRangeSliderView').tag(sync=True)
|
||||
_model_name = Unicode('FloatRangeSliderModel').tag(sync=True)
|
||||
step = CFloat(0.1, help="Minimum step to increment the value").tag(sync=True)
|
||||
orientation = CaselessStrEnum(values=['horizontal', 'vertical'],
|
||||
default_value='horizontal', help="Vertical or horizontal.").tag(sync=True)
|
||||
readout = Bool(True, help="Display the current value of the slider next to it.").tag(sync=True)
|
||||
readout_format = NumberFormat(
|
||||
'.2f', help="Format for the readout").tag(sync=True)
|
||||
continuous_update = Bool(True, help="Update the value of the widget as the user is sliding the slider.").tag(sync=True)
|
||||
disabled = Bool(False, help="Enable or disable user changes").tag(sync=True)
|
||||
|
||||
style = InstanceDict(SliderStyle).tag(sync=True, **widget_serialization)
|
294
venv/Lib/site-packages/ipywidgets/widgets/widget_int.py
Normal file
294
venv/Lib/site-packages/ipywidgets/widgets/widget_int.py
Normal file
|
@ -0,0 +1,294 @@
|
|||
# Copyright (c) Jupyter Development Team.
|
||||
# Distributed under the terms of the Modified BSD License.
|
||||
|
||||
"""Int class.
|
||||
|
||||
Represents an unbounded int using a widget.
|
||||
"""
|
||||
|
||||
from .widget_description import DescriptionWidget, DescriptionStyle
|
||||
from .valuewidget import ValueWidget
|
||||
from .widget import register, widget_serialization
|
||||
from .widget_core import CoreWidget
|
||||
from traitlets import Instance
|
||||
from .trait_types import Color, InstanceDict, NumberFormat
|
||||
from traitlets import (
|
||||
Unicode, CInt, Bool, CaselessStrEnum, Tuple, TraitError, default, validate
|
||||
)
|
||||
|
||||
_int_doc_t = """
|
||||
Parameters
|
||||
----------
|
||||
value: integer
|
||||
The initial value.
|
||||
"""
|
||||
|
||||
_bounded_int_doc_t = """
|
||||
Parameters
|
||||
----------
|
||||
value: integer
|
||||
The initial value.
|
||||
min: integer
|
||||
The lower limit for the value.
|
||||
max: integer
|
||||
The upper limit for the value.
|
||||
step: integer
|
||||
The step between allowed values.
|
||||
"""
|
||||
|
||||
def _int_doc(cls):
|
||||
"""Add int docstring template to class init."""
|
||||
def __init__(self, value=None, **kwargs):
|
||||
if value is not None:
|
||||
kwargs['value'] = value
|
||||
super(cls, self).__init__(**kwargs)
|
||||
|
||||
__init__.__doc__ = _int_doc_t
|
||||
cls.__init__ = __init__
|
||||
return cls
|
||||
|
||||
def _bounded_int_doc(cls):
|
||||
"""Add bounded int docstring template to class init."""
|
||||
def __init__(self, value=None, min=None, max=None, step=None, **kwargs):
|
||||
if value is not None:
|
||||
kwargs['value'] = value
|
||||
if min is not None:
|
||||
kwargs['min'] = min
|
||||
if max is not None:
|
||||
kwargs['max'] = max
|
||||
if step is not None:
|
||||
kwargs['step'] = step
|
||||
super(cls, self).__init__(**kwargs)
|
||||
|
||||
__init__.__doc__ = _bounded_int_doc_t
|
||||
cls.__init__ = __init__
|
||||
return cls
|
||||
|
||||
|
||||
class _Int(DescriptionWidget, ValueWidget, CoreWidget):
|
||||
"""Base class for widgets that represent an integer."""
|
||||
value = CInt(0, help="Int value").tag(sync=True)
|
||||
|
||||
def __init__(self, value=None, **kwargs):
|
||||
if value is not None:
|
||||
kwargs['value'] = value
|
||||
super(_Int, self).__init__(**kwargs)
|
||||
|
||||
|
||||
class _BoundedInt(_Int):
|
||||
"""Base class for widgets that represent an integer bounded from above and below.
|
||||
"""
|
||||
max = CInt(100, help="Max value").tag(sync=True)
|
||||
min = CInt(0, help="Min value").tag(sync=True)
|
||||
|
||||
def __init__(self, value=None, min=None, max=None, step=None, **kwargs):
|
||||
if value is not None:
|
||||
kwargs['value'] = value
|
||||
if min is not None:
|
||||
kwargs['min'] = min
|
||||
if max is not None:
|
||||
kwargs['max'] = max
|
||||
if step is not None:
|
||||
kwargs['step'] = step
|
||||
super(_BoundedInt, self).__init__(**kwargs)
|
||||
|
||||
@validate('value')
|
||||
def _validate_value(self, proposal):
|
||||
"""Cap and floor value"""
|
||||
value = proposal['value']
|
||||
if self.min > value or self.max < value:
|
||||
value = min(max(value, self.min), self.max)
|
||||
return value
|
||||
|
||||
@validate('min')
|
||||
def _validate_min(self, proposal):
|
||||
"""Enforce min <= value <= max"""
|
||||
min = proposal['value']
|
||||
if min > self.max:
|
||||
raise TraitError('setting min > max')
|
||||
if min > self.value:
|
||||
self.value = min
|
||||
return min
|
||||
|
||||
@validate('max')
|
||||
def _validate_max(self, proposal):
|
||||
"""Enforce min <= value <= max"""
|
||||
max = proposal['value']
|
||||
if max < self.min:
|
||||
raise TraitError('setting max < min')
|
||||
if max < self.value:
|
||||
self.value = max
|
||||
return max
|
||||
|
||||
@register
|
||||
@_int_doc
|
||||
class IntText(_Int):
|
||||
"""Textbox widget that represents an integer."""
|
||||
_view_name = Unicode('IntTextView').tag(sync=True)
|
||||
_model_name = Unicode('IntTextModel').tag(sync=True)
|
||||
disabled = Bool(False, help="Enable or disable user changes").tag(sync=True)
|
||||
continuous_update = Bool(False, help="Update the value as the user types. If False, update on submission, e.g., pressing Enter or navigating away.").tag(sync=True)
|
||||
step = CInt(1, help="Minimum step to increment the value").tag(sync=True)
|
||||
|
||||
|
||||
@register
|
||||
@_bounded_int_doc
|
||||
class BoundedIntText(_BoundedInt):
|
||||
"""Textbox widget that represents an integer bounded from above and below.
|
||||
"""
|
||||
_view_name = Unicode('IntTextView').tag(sync=True)
|
||||
_model_name = Unicode('BoundedIntTextModel').tag(sync=True)
|
||||
disabled = Bool(False, help="Enable or disable user changes").tag(sync=True)
|
||||
continuous_update = Bool(False, help="Update the value as the user types. If False, update on submission, e.g., pressing Enter or navigating away.").tag(sync=True)
|
||||
step = CInt(1, help="Minimum step to increment the value").tag(sync=True)
|
||||
|
||||
|
||||
@register
|
||||
class SliderStyle(DescriptionStyle, CoreWidget):
|
||||
"""Button style widget."""
|
||||
_model_name = Unicode('SliderStyleModel').tag(sync=True)
|
||||
handle_color = Color(None, allow_none=True, help="Color of the slider handle.").tag(sync=True)
|
||||
|
||||
|
||||
@register
|
||||
@_bounded_int_doc
|
||||
class IntSlider(_BoundedInt):
|
||||
"""Slider widget that represents an integer bounded from above and below.
|
||||
"""
|
||||
_view_name = Unicode('IntSliderView').tag(sync=True)
|
||||
_model_name = Unicode('IntSliderModel').tag(sync=True)
|
||||
step = CInt(1, help="Minimum step to increment the value").tag(sync=True)
|
||||
orientation = CaselessStrEnum(values=['horizontal', 'vertical'],
|
||||
default_value='horizontal', help="Vertical or horizontal.").tag(sync=True)
|
||||
readout = Bool(True, help="Display the current value of the slider next to it.").tag(sync=True)
|
||||
readout_format = NumberFormat(
|
||||
'd', help="Format for the readout").tag(sync=True)
|
||||
continuous_update = Bool(True, help="Update the value of the widget as the user is holding the slider.").tag(sync=True)
|
||||
disabled = Bool(False, help="Enable or disable user changes").tag(sync=True)
|
||||
|
||||
style = InstanceDict(SliderStyle).tag(sync=True, **widget_serialization)
|
||||
|
||||
|
||||
@register
|
||||
class ProgressStyle(DescriptionStyle, CoreWidget):
|
||||
"""Button style widget."""
|
||||
_model_name = Unicode('ProgressStyleModel').tag(sync=True)
|
||||
bar_color = Color(None, allow_none=True, help="Color of the progress bar.").tag(sync=True)
|
||||
|
||||
|
||||
@register
|
||||
@_bounded_int_doc
|
||||
class IntProgress(_BoundedInt):
|
||||
"""Progress bar that represents an integer bounded from above and below.
|
||||
"""
|
||||
_view_name = Unicode('ProgressView').tag(sync=True)
|
||||
_model_name = Unicode('IntProgressModel').tag(sync=True)
|
||||
orientation = CaselessStrEnum(values=['horizontal', 'vertical'],
|
||||
default_value='horizontal', help="Vertical or horizontal.").tag(sync=True)
|
||||
|
||||
bar_style = CaselessStrEnum(
|
||||
values=['success', 'info', 'warning', 'danger', ''], default_value='',
|
||||
help="""Use a predefined styling for the progess bar.""").tag(sync=True)
|
||||
|
||||
style = InstanceDict(ProgressStyle).tag(sync=True, **widget_serialization)
|
||||
|
||||
|
||||
class _IntRange(_Int):
|
||||
value = Tuple(CInt(), CInt(), default_value=(0, 1),
|
||||
help="Tuple of (lower, upper) bounds").tag(sync=True)
|
||||
|
||||
@property
|
||||
def lower(self):
|
||||
return self.value[0]
|
||||
|
||||
@lower.setter
|
||||
def lower(self, lower):
|
||||
self.value = (lower, self.value[1])
|
||||
|
||||
@property
|
||||
def upper(self):
|
||||
return self.value[1]
|
||||
|
||||
@upper.setter
|
||||
def upper(self, upper):
|
||||
self.value = (self.value[0], upper)
|
||||
|
||||
@validate('value')
|
||||
def _validate_value(self, proposal):
|
||||
lower, upper = proposal['value']
|
||||
if upper < lower:
|
||||
raise TraitError('setting lower > upper')
|
||||
return lower, upper
|
||||
|
||||
@register
|
||||
class Play(_BoundedInt):
|
||||
"""Play/repeat buttons to step through values automatically, and optionally loop.
|
||||
"""
|
||||
interval = CInt(100, help="The maximum value for the play control.").tag(sync=True)
|
||||
step = CInt(1, help="Increment step").tag(sync=True)
|
||||
disabled = Bool(False, help="Enable or disable user changes").tag(sync=True)
|
||||
|
||||
_view_name = Unicode('PlayView').tag(sync=True)
|
||||
_model_name = Unicode('PlayModel').tag(sync=True)
|
||||
|
||||
_playing = Bool(help="Whether the control is currently playing.").tag(sync=True)
|
||||
_repeat = Bool(help="Whether the control will repeat in a continous loop.").tag(sync=True)
|
||||
show_repeat = Bool(True, help="Show the repeat toggle button in the widget.").tag(sync=True)
|
||||
|
||||
class _BoundedIntRange(_IntRange):
|
||||
max = CInt(100, help="Max value").tag(sync=True)
|
||||
min = CInt(0, help="Min value").tag(sync=True)
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
min, max = kwargs.get('min', 0), kwargs.get('max', 100)
|
||||
if not kwargs.get('value', None):
|
||||
kwargs['value'] = (0.75 * min + 0.25 * max,
|
||||
0.25 * min + 0.75 * max)
|
||||
super(_BoundedIntRange, self).__init__(*args, **kwargs)
|
||||
|
||||
@validate('min', 'max')
|
||||
def _validate_bounds(self, proposal):
|
||||
trait = proposal['trait']
|
||||
new = proposal['value']
|
||||
if trait.name == 'min' and new > self.max:
|
||||
raise TraitError('setting min > max')
|
||||
if trait.name == 'max' and new < self.min:
|
||||
raise TraitError('setting max < min')
|
||||
if trait.name == 'min':
|
||||
self.value = (max(new, self.value[0]), max(new, self.value[1]))
|
||||
if trait.name == 'max':
|
||||
self.value = (min(new, self.value[0]), min(new, self.value[1]))
|
||||
return new
|
||||
|
||||
@validate('value')
|
||||
def _validate_value(self, proposal):
|
||||
lower, upper = super(_BoundedIntRange, self)._validate_value(proposal)
|
||||
lower, upper = min(lower, self.max), min(upper, self.max)
|
||||
lower, upper = max(lower, self.min), max(upper, self.min)
|
||||
return lower, upper
|
||||
|
||||
|
||||
@register
|
||||
class IntRangeSlider(_BoundedIntRange):
|
||||
"""Slider/trackbar that represents a pair of ints bounded by minimum and maximum value.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
value : int tuple
|
||||
The pair (`lower`, `upper`) of integers
|
||||
min : int
|
||||
The lowest allowed value for `lower`
|
||||
max : int
|
||||
The highest allowed value for `upper`
|
||||
"""
|
||||
_view_name = Unicode('IntRangeSliderView').tag(sync=True)
|
||||
_model_name = Unicode('IntRangeSliderModel').tag(sync=True)
|
||||
step = CInt(1, help="Minimum step that the value can take").tag(sync=True)
|
||||
orientation = CaselessStrEnum(values=['horizontal', 'vertical'],
|
||||
default_value='horizontal', help="Vertical or horizontal.").tag(sync=True)
|
||||
readout = Bool(True, help="Display the current value of the slider next to it.").tag(sync=True)
|
||||
readout_format = NumberFormat(
|
||||
'd', help="Format for the readout").tag(sync=True)
|
||||
continuous_update = Bool(True, help="Update the value of the widget as the user is sliding the slider.").tag(sync=True)
|
||||
style = InstanceDict(SliderStyle, help="Slider style customizations.").tag(sync=True, **widget_serialization)
|
||||
disabled = Bool(False, help="Enable or disable user changes").tag(sync=True)
|
96
venv/Lib/site-packages/ipywidgets/widgets/widget_layout.py
Normal file
96
venv/Lib/site-packages/ipywidgets/widgets/widget_layout.py
Normal file
|
@ -0,0 +1,96 @@
|
|||
# Copyright (c) Jupyter Development Team.
|
||||
# Distributed under the terms of the Modified BSD License.
|
||||
|
||||
"""Contains the Layout class"""
|
||||
|
||||
from traitlets import Unicode, Instance, CaselessStrEnum, validate
|
||||
from .widget import Widget, register
|
||||
from .._version import __jupyter_widgets_base_version__
|
||||
|
||||
CSS_PROPERTIES=['inherit', 'initial', 'unset']
|
||||
|
||||
@register
|
||||
class Layout(Widget):
|
||||
"""Layout specification
|
||||
|
||||
Defines a layout that can be expressed using CSS. Supports a subset of
|
||||
https://developer.mozilla.org/en-US/docs/Web/CSS/Reference
|
||||
|
||||
When a property is also accessible via a shorthand property, we only
|
||||
expose the shorthand.
|
||||
|
||||
For example:
|
||||
- ``flex-grow``, ``flex-shrink`` and ``flex-basis`` are bound to ``flex``.
|
||||
- ``flex-wrap`` and ``flex-direction`` are bound to ``flex-flow``.
|
||||
- ``margin-[top/bottom/left/right]`` values are bound to ``margin``, etc.
|
||||
"""
|
||||
|
||||
_view_name = Unicode('LayoutView').tag(sync=True)
|
||||
_view_module = Unicode('@jupyter-widgets/base').tag(sync=True)
|
||||
_view_module_version = Unicode(__jupyter_widgets_base_version__).tag(sync=True)
|
||||
_model_name = Unicode('LayoutModel').tag(sync=True)
|
||||
|
||||
# Keys
|
||||
align_content = CaselessStrEnum(['flex-start', 'flex-end', 'center', 'space-between',
|
||||
'space-around', 'space-evenly', 'stretch'] + CSS_PROPERTIES, allow_none=True, help="The align-content CSS attribute.").tag(sync=True)
|
||||
align_items = CaselessStrEnum(['flex-start', 'flex-end', 'center',
|
||||
'baseline', 'stretch'] + CSS_PROPERTIES, allow_none=True, help="The align-items CSS attribute.").tag(sync=True)
|
||||
align_self = CaselessStrEnum(['auto', 'flex-start', 'flex-end',
|
||||
'center', 'baseline', 'stretch'] + CSS_PROPERTIES, allow_none=True, help="The align-self CSS attribute.").tag(sync=True)
|
||||
bottom = Unicode(None, allow_none=True, help="The bottom CSS attribute.").tag(sync=True)
|
||||
border = Unicode(None, allow_none=True, help="The border CSS attribute.").tag(sync=True)
|
||||
display = Unicode(None, allow_none=True, help="The display CSS attribute.").tag(sync=True)
|
||||
flex = Unicode(None, allow_none=True, help="The flex CSS attribute.").tag(sync=True)
|
||||
flex_flow = Unicode(None, allow_none=True, help="The flex-flow CSS attribute.").tag(sync=True)
|
||||
height = Unicode(None, allow_none=True, help="The height CSS attribute.").tag(sync=True)
|
||||
justify_content = CaselessStrEnum(['flex-start', 'flex-end', 'center',
|
||||
'space-between', 'space-around'] + CSS_PROPERTIES, allow_none=True, help="The justify-content CSS attribute.").tag(sync=True)
|
||||
justify_items = CaselessStrEnum(['flex-start', 'flex-end', 'center'] + CSS_PROPERTIES,
|
||||
allow_none=True, help="The justify-items CSS attribute.").tag(sync=True)
|
||||
left = Unicode(None, allow_none=True, help="The left CSS attribute.").tag(sync=True)
|
||||
margin = Unicode(None, allow_none=True, help="The margin CSS attribute.").tag(sync=True)
|
||||
max_height = Unicode(None, allow_none=True, help="The max-height CSS attribute.").tag(sync=True)
|
||||
max_width = Unicode(None, allow_none=True, help="The max-width CSS attribute.").tag(sync=True)
|
||||
min_height = Unicode(None, allow_none=True, help="The min-height CSS attribute.").tag(sync=True)
|
||||
min_width = Unicode(None, allow_none=True, help="The min-width CSS attribute.").tag(sync=True)
|
||||
overflow = Unicode(None, allow_none=True, help="The overflow CSS attribute.").tag(sync=True)
|
||||
overflow_x = CaselessStrEnum(['visible', 'hidden', 'scroll', 'auto'] + CSS_PROPERTIES, allow_none=True, help="The overflow-x CSS attribute (deprecated).").tag(sync=True)
|
||||
overflow_y = CaselessStrEnum(['visible', 'hidden', 'scroll', 'auto'] + CSS_PROPERTIES, allow_none=True, help="The overflow-y CSS attribute (deprecated).").tag(sync=True)
|
||||
order = Unicode(None, allow_none=True, help="The order CSS attribute.").tag(sync=True)
|
||||
padding = Unicode(None, allow_none=True, help="The padding CSS attribute.").tag(sync=True)
|
||||
right = Unicode(None, allow_none=True, help="The right CSS attribute.").tag(sync=True)
|
||||
top = Unicode(None, allow_none=True, help="The top CSS attribute.").tag(sync=True)
|
||||
visibility = CaselessStrEnum(['visible', 'hidden']+CSS_PROPERTIES, allow_none=True, help="The visibility CSS attribute.").tag(sync=True)
|
||||
width = Unicode(None, allow_none=True, help="The width CSS attribute.").tag(sync=True)
|
||||
|
||||
object_fit = CaselessStrEnum(['contain', 'cover', 'fill', 'scale-down', 'none'], allow_none=True, help="The object-fit CSS attribute.").tag(sync=True)
|
||||
object_position = Unicode(None, allow_none=True, help="The object-position CSS attribute.").tag(sync=True)
|
||||
|
||||
grid_auto_columns = Unicode(None, allow_none=True, help="The grid-auto-columns CSS attribute.").tag(sync=True)
|
||||
grid_auto_flow = CaselessStrEnum(['column','row','row dense','column dense']+ CSS_PROPERTIES, allow_none=True, help="The grid-auto-flow CSS attribute.").tag(sync=True)
|
||||
grid_auto_rows = Unicode(None, allow_none=True, help="The grid-auto-rows CSS attribute.").tag(sync=True)
|
||||
grid_gap = Unicode(None, allow_none=True, help="The grid-gap CSS attribute.").tag(sync=True)
|
||||
grid_template_rows = Unicode(None, allow_none=True, help="The grid-template-rows CSS attribute.").tag(sync=True)
|
||||
grid_template_columns = Unicode(None, allow_none=True, help="The grid-template-columns CSS attribute.").tag(sync=True)
|
||||
grid_template_areas = Unicode(None, allow_none=True, help="The grid-template-areas CSS attribute.").tag(sync=True)
|
||||
grid_row = Unicode(None, allow_none=True, help="The grid-row CSS attribute.").tag(sync=True)
|
||||
grid_column = Unicode(None, allow_none=True, help="The grid-column CSS attribute.").tag(sync=True)
|
||||
grid_area = Unicode(None, allow_none=True, help="The grid-area CSS attribute.").tag(sync=True)
|
||||
|
||||
@validate('overflow_x', 'overflow_y')
|
||||
def _validate_overflows(self, proposal):
|
||||
if proposal.value is not None:
|
||||
import warnings
|
||||
warnings.warn("Layout properties overflow_x and overflow_y have been deprecated and will be dropped in a future release. Please use the overflow shorthand property instead", DeprecationWarning)
|
||||
return proposal.value
|
||||
|
||||
|
||||
class LayoutTraitType(Instance):
|
||||
|
||||
klass = Layout
|
||||
|
||||
def validate(self, obj, value):
|
||||
if isinstance(value, dict):
|
||||
return super(LayoutTraitType, self).validate(obj, self.klass(**value))
|
||||
else:
|
||||
return super(LayoutTraitType, self).validate(obj, value)
|
105
venv/Lib/site-packages/ipywidgets/widgets/widget_link.py
Normal file
105
venv/Lib/site-packages/ipywidgets/widgets/widget_link.py
Normal file
|
@ -0,0 +1,105 @@
|
|||
# Copyright (c) Jupyter Development Team.
|
||||
# Distributed under the terms of the Modified BSD License.
|
||||
|
||||
"""Link and DirectionalLink classes.
|
||||
|
||||
Propagate changes between widgets on the javascript side.
|
||||
"""
|
||||
|
||||
from .widget import Widget, register, widget_serialization
|
||||
from .widget_core import CoreWidget
|
||||
|
||||
from traitlets import Unicode, Tuple, Instance, TraitError
|
||||
|
||||
|
||||
class WidgetTraitTuple(Tuple):
|
||||
"""Traitlet for validating a single (Widget, 'trait_name') pair"""
|
||||
|
||||
info_text = "A (Widget, 'trait_name') pair"
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
super(WidgetTraitTuple, self).__init__(Instance(Widget), Unicode(), **kwargs)
|
||||
|
||||
def validate_elements(self, obj, value):
|
||||
value = super(WidgetTraitTuple, self).validate_elements(obj, value)
|
||||
widget, trait_name = value
|
||||
trait = widget.traits().get(trait_name)
|
||||
trait_repr = "%s.%s" % (widget.__class__.__name__, trait_name)
|
||||
# Can't raise TraitError because the parent will swallow the message
|
||||
# and throw it away in a new, less informative TraitError
|
||||
if trait is None:
|
||||
raise TypeError("No such trait: %s" % trait_repr)
|
||||
elif not trait.metadata.get('sync'):
|
||||
raise TypeError("%s cannot be synced" % trait_repr)
|
||||
return value
|
||||
|
||||
|
||||
@register
|
||||
class Link(CoreWidget):
|
||||
"""Link Widget
|
||||
|
||||
source: a (Widget, 'trait_name') tuple for the source trait
|
||||
target: a (Widget, 'trait_name') tuple that should be updated
|
||||
"""
|
||||
|
||||
_model_name = Unicode('LinkModel').tag(sync=True)
|
||||
target = WidgetTraitTuple(help="The target (widget, 'trait_name') pair").tag(sync=True, **widget_serialization)
|
||||
source = WidgetTraitTuple(help="The source (widget, 'trait_name') pair").tag(sync=True, **widget_serialization)
|
||||
|
||||
def __init__(self, source, target, **kwargs):
|
||||
kwargs['source'] = source
|
||||
kwargs['target'] = target
|
||||
super(Link, self).__init__(**kwargs)
|
||||
|
||||
# for compatibility with traitlet links
|
||||
def unlink(self):
|
||||
self.close()
|
||||
|
||||
|
||||
def jslink(attr1, attr2):
|
||||
"""Link two widget attributes on the frontend so they remain in sync.
|
||||
|
||||
The link is created in the front-end and does not rely on a roundtrip
|
||||
to the backend.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
source : a (Widget, 'trait_name') tuple for the first trait
|
||||
target : a (Widget, 'trait_name') tuple for the second trait
|
||||
|
||||
Examples
|
||||
--------
|
||||
|
||||
>>> c = link((widget1, 'value'), (widget2, 'value'))
|
||||
"""
|
||||
return Link(attr1, attr2)
|
||||
|
||||
|
||||
@register
|
||||
class DirectionalLink(Link):
|
||||
"""A directional link
|
||||
|
||||
source: a (Widget, 'trait_name') tuple for the source trait
|
||||
target: a (Widget, 'trait_name') tuple that should be updated
|
||||
when the source trait changes.
|
||||
"""
|
||||
_model_name = Unicode('DirectionalLinkModel').tag(sync=True)
|
||||
|
||||
|
||||
def jsdlink(source, target):
|
||||
"""Link a source widget attribute with a target widget attribute.
|
||||
|
||||
The link is created in the front-end and does not rely on a roundtrip
|
||||
to the backend.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
source : a (Widget, 'trait_name') tuple for the source trait
|
||||
target : a (Widget, 'trait_name') tuple for the target trait
|
||||
|
||||
Examples
|
||||
--------
|
||||
|
||||
>>> c = dlink((src_widget, 'value'), (tgt_widget, 'value'))
|
||||
"""
|
||||
return DirectionalLink(source, target)
|
226
venv/Lib/site-packages/ipywidgets/widgets/widget_media.py
Normal file
226
venv/Lib/site-packages/ipywidgets/widgets/widget_media.py
Normal file
|
@ -0,0 +1,226 @@
|
|||
# Copyright (c) Jupyter Development Team.
|
||||
# Distributed under the terms of the Modified BSD License.
|
||||
|
||||
import mimetypes
|
||||
|
||||
from .widget_core import CoreWidget
|
||||
from .domwidget import DOMWidget
|
||||
from .valuewidget import ValueWidget
|
||||
from .widget import register
|
||||
from traitlets import Unicode, CUnicode, Bytes, Bool
|
||||
from .trait_types import bytes_serialization
|
||||
from .util import text_type
|
||||
|
||||
|
||||
@register
|
||||
class _Media(DOMWidget, ValueWidget, CoreWidget):
|
||||
"""Base class for Image, Audio and Video widgets.
|
||||
|
||||
The `value` of this widget accepts a byte string. The byte string is the
|
||||
raw data that you want the browser to display.
|
||||
|
||||
If you pass `"url"` to the `"format"` trait, `value` will be interpreted
|
||||
as a URL as bytes encoded in UTF-8.
|
||||
"""
|
||||
|
||||
# Define the custom state properties to sync with the front-end
|
||||
value = Bytes(help="The media data as a byte string.").tag(sync=True, **bytes_serialization)
|
||||
|
||||
@classmethod
|
||||
def _from_file(cls, tag, filename, **kwargs):
|
||||
"""
|
||||
Create an :class:`Media` from a local file.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
filename: str
|
||||
The location of a file to read into the value from disk.
|
||||
|
||||
**kwargs:
|
||||
The keyword arguments for `Media`
|
||||
|
||||
Returns an `Media` with the value set from the filename.
|
||||
"""
|
||||
value = cls._load_file_value(filename)
|
||||
|
||||
if 'format' not in kwargs:
|
||||
format = cls._guess_format(tag, filename)
|
||||
if format is not None:
|
||||
kwargs['format'] = format
|
||||
|
||||
return cls(value=value, **kwargs)
|
||||
|
||||
@classmethod
|
||||
def from_url(cls, url, **kwargs):
|
||||
"""
|
||||
Create an :class:`Media` from a URL.
|
||||
|
||||
:code:`Media.from_url(url)` is equivalent to:
|
||||
|
||||
.. code-block: python
|
||||
|
||||
med = Media(value=url, format='url')
|
||||
|
||||
But both unicode and bytes arguments are allowed for ``url``.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
url: [str, bytes]
|
||||
The location of a URL to load.
|
||||
"""
|
||||
if isinstance(url, text_type):
|
||||
# If unicode (str in Python 3), it needs to be encoded to bytes
|
||||
url = url.encode('utf-8')
|
||||
|
||||
return cls(value=url, format='url')
|
||||
|
||||
def set_value_from_file(self, filename):
|
||||
"""
|
||||
Convenience method for reading a file into `value`.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
filename: str
|
||||
The location of a file to read into value from disk.
|
||||
"""
|
||||
value = self._load_file_value(filename)
|
||||
|
||||
self.value = value
|
||||
|
||||
@classmethod
|
||||
def _load_file_value(cls, filename):
|
||||
if getattr(filename, 'read', None) is not None:
|
||||
return filename.read()
|
||||
else:
|
||||
with open(filename, 'rb') as f:
|
||||
return f.read()
|
||||
|
||||
@classmethod
|
||||
def _guess_format(cls, tag, filename):
|
||||
# file objects may have a .name parameter
|
||||
name = getattr(filename, 'name', None)
|
||||
name = name or filename
|
||||
|
||||
try:
|
||||
mtype, _ = mimetypes.guess_type(name)
|
||||
if not mtype.startswith('{}/'.format(tag)):
|
||||
return None
|
||||
|
||||
return mtype[len('{}/'.format(tag)):]
|
||||
except Exception:
|
||||
return None
|
||||
|
||||
def _get_repr(self, cls):
|
||||
# Truncate the value in the repr, since it will
|
||||
# typically be very, very large.
|
||||
class_name = self.__class__.__name__
|
||||
|
||||
# Return value first like a ValueWidget
|
||||
signature = []
|
||||
sig_value = repr(self.value)
|
||||
prefix, rest = sig_value.split("'", 1)
|
||||
content = rest[:-1]
|
||||
if len(content) > 100:
|
||||
sig_value = "{}'{}...'".format(prefix, content[0:100])
|
||||
signature.append('%s=%s' % ('value', sig_value))
|
||||
|
||||
for key in super(cls, self)._repr_keys():
|
||||
if key == 'value':
|
||||
continue
|
||||
value = str(getattr(self, key))
|
||||
signature.append('%s=%r' % (key, value))
|
||||
signature = ', '.join(signature)
|
||||
return '%s(%s)' % (class_name, signature)
|
||||
|
||||
|
||||
@register
|
||||
class Image(_Media):
|
||||
"""Displays an image as a widget.
|
||||
|
||||
The `value` of this widget accepts a byte string. The byte string is the
|
||||
raw image data that you want the browser to display. You can explicitly
|
||||
define the format of the byte string using the `format` trait (which
|
||||
defaults to "png").
|
||||
|
||||
If you pass `"url"` to the `"format"` trait, `value` will be interpreted
|
||||
as a URL as bytes encoded in UTF-8.
|
||||
"""
|
||||
_view_name = Unicode('ImageView').tag(sync=True)
|
||||
_model_name = Unicode('ImageModel').tag(sync=True)
|
||||
|
||||
# Define the custom state properties to sync with the front-end
|
||||
format = Unicode('png', help="The format of the image.").tag(sync=True)
|
||||
width = CUnicode(help="Width of the image in pixels. Use layout.width "
|
||||
"for styling the widget.").tag(sync=True)
|
||||
height = CUnicode(help="Height of the image in pixels. Use layout.height "
|
||||
"for styling the widget.").tag(sync=True)
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(Image, self).__init__(*args, **kwargs)
|
||||
|
||||
@classmethod
|
||||
def from_file(cls, filename, **kwargs):
|
||||
return cls._from_file('image', filename, **kwargs)
|
||||
|
||||
def __repr__(self):
|
||||
return self._get_repr(Image)
|
||||
|
||||
|
||||
@register
|
||||
class Video(_Media):
|
||||
"""Displays a video as a widget.
|
||||
|
||||
The `value` of this widget accepts a byte string. The byte string is the
|
||||
raw video data that you want the browser to display. You can explicitly
|
||||
define the format of the byte string using the `format` trait (which
|
||||
defaults to "mp4").
|
||||
|
||||
If you pass `"url"` to the `"format"` trait, `value` will be interpreted
|
||||
as a URL as bytes encoded in UTF-8.
|
||||
"""
|
||||
_view_name = Unicode('VideoView').tag(sync=True)
|
||||
_model_name = Unicode('VideoModel').tag(sync=True)
|
||||
|
||||
# Define the custom state properties to sync with the front-end
|
||||
format = Unicode('mp4', help="The format of the video.").tag(sync=True)
|
||||
width = CUnicode(help="Width of the video in pixels.").tag(sync=True)
|
||||
height = CUnicode(help="Height of the video in pixels.").tag(sync=True)
|
||||
autoplay = Bool(True, help="When true, the video starts when it's displayed").tag(sync=True)
|
||||
loop = Bool(True, help="When true, the video will start from the beginning after finishing").tag(sync=True)
|
||||
controls = Bool(True, help="Specifies that video controls should be displayed (such as a play/pause button etc)").tag(sync=True)
|
||||
|
||||
@classmethod
|
||||
def from_file(cls, filename, **kwargs):
|
||||
return cls._from_file('video', filename, **kwargs)
|
||||
|
||||
def __repr__(self):
|
||||
return self._get_repr(Video)
|
||||
|
||||
|
||||
@register
|
||||
class Audio(_Media):
|
||||
"""Displays a audio as a widget.
|
||||
|
||||
The `value` of this widget accepts a byte string. The byte string is the
|
||||
raw audio data that you want the browser to display. You can explicitly
|
||||
define the format of the byte string using the `format` trait (which
|
||||
defaults to "mp3").
|
||||
|
||||
If you pass `"url"` to the `"format"` trait, `value` will be interpreted
|
||||
as a URL as bytes encoded in UTF-8.
|
||||
"""
|
||||
_view_name = Unicode('AudioView').tag(sync=True)
|
||||
_model_name = Unicode('AudioModel').tag(sync=True)
|
||||
|
||||
# Define the custom state properties to sync with the front-end
|
||||
format = Unicode('mp3', help="The format of the audio.").tag(sync=True)
|
||||
autoplay = Bool(True, help="When true, the audio starts when it's displayed").tag(sync=True)
|
||||
loop = Bool(True, help="When true, the audio will start from the beginning after finishing").tag(sync=True)
|
||||
controls = Bool(True, help="Specifies that audio controls should be displayed (such as a play/pause button etc)").tag(sync=True)
|
||||
|
||||
@classmethod
|
||||
def from_file(cls, filename, **kwargs):
|
||||
return cls._from_file('audio', filename, **kwargs)
|
||||
|
||||
def __repr__(self):
|
||||
return self._get_repr(Audio)
|
165
venv/Lib/site-packages/ipywidgets/widgets/widget_output.py
Normal file
165
venv/Lib/site-packages/ipywidgets/widgets/widget_output.py
Normal file
|
@ -0,0 +1,165 @@
|
|||
# Copyright (c) Jupyter Development Team.
|
||||
# Distributed under the terms of the Modified BSD License.
|
||||
|
||||
"""Output class.
|
||||
|
||||
Represents a widget that can be used to display output within the widget area.
|
||||
"""
|
||||
|
||||
import sys
|
||||
from functools import wraps
|
||||
|
||||
from .domwidget import DOMWidget
|
||||
from .trait_types import TypedTuple
|
||||
from .widget import register
|
||||
from .._version import __jupyter_widgets_output_version__
|
||||
|
||||
from traitlets import Unicode, Dict
|
||||
from IPython.core.interactiveshell import InteractiveShell
|
||||
from IPython.display import clear_output
|
||||
from IPython import get_ipython
|
||||
|
||||
|
||||
@register
|
||||
class Output(DOMWidget):
|
||||
"""Widget used as a context manager to display output.
|
||||
|
||||
This widget can capture and display stdout, stderr, and rich output. To use
|
||||
it, create an instance of it and display it.
|
||||
|
||||
You can then use the widget as a context manager: any output produced while in the
|
||||
context will be captured and displayed in the widget instead of the standard output
|
||||
area.
|
||||
|
||||
You can also use the .capture() method to decorate a function or a method. Any output
|
||||
produced by the function will then go to the output widget. This is useful for
|
||||
debugging widget callbacks, for example.
|
||||
|
||||
Example::
|
||||
import ipywidgets as widgets
|
||||
from IPython.display import display
|
||||
out = widgets.Output()
|
||||
display(out)
|
||||
|
||||
print('prints to output area')
|
||||
|
||||
with out:
|
||||
print('prints to output widget')
|
||||
|
||||
@out.capture()
|
||||
def func():
|
||||
print('prints to output widget')
|
||||
"""
|
||||
_view_name = Unicode('OutputView').tag(sync=True)
|
||||
_model_name = Unicode('OutputModel').tag(sync=True)
|
||||
_view_module = Unicode('@jupyter-widgets/output').tag(sync=True)
|
||||
_model_module = Unicode('@jupyter-widgets/output').tag(sync=True)
|
||||
_view_module_version = Unicode(__jupyter_widgets_output_version__).tag(sync=True)
|
||||
_model_module_version = Unicode(__jupyter_widgets_output_version__).tag(sync=True)
|
||||
|
||||
msg_id = Unicode('', help="Parent message id of messages to capture").tag(sync=True)
|
||||
outputs = TypedTuple(trait=Dict(), help="The output messages synced from the frontend.").tag(sync=True)
|
||||
|
||||
__counter = 0
|
||||
|
||||
def clear_output(self, *pargs, **kwargs):
|
||||
"""
|
||||
Clear the content of the output widget.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
|
||||
wait: bool
|
||||
If True, wait to clear the output until new output is
|
||||
available to replace it. Default: False
|
||||
"""
|
||||
with self:
|
||||
clear_output(*pargs, **kwargs)
|
||||
|
||||
# PY3: Force passing clear_output and clear_kwargs as kwargs
|
||||
def capture(self, clear_output=False, *clear_args, **clear_kwargs):
|
||||
"""
|
||||
Decorator to capture the stdout and stderr of a function.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
|
||||
clear_output: bool
|
||||
If True, clear the content of the output widget at every
|
||||
new function call. Default: False
|
||||
|
||||
wait: bool
|
||||
If True, wait to clear the output until new output is
|
||||
available to replace it. This is only used if clear_output
|
||||
is also True.
|
||||
Default: False
|
||||
"""
|
||||
def capture_decorator(func):
|
||||
@wraps(func)
|
||||
def inner(*args, **kwargs):
|
||||
if clear_output:
|
||||
self.clear_output(*clear_args, **clear_kwargs)
|
||||
with self:
|
||||
return func(*args, **kwargs)
|
||||
return inner
|
||||
return capture_decorator
|
||||
|
||||
def __enter__(self):
|
||||
"""Called upon entering output widget context manager."""
|
||||
self._flush()
|
||||
ip = get_ipython()
|
||||
if ip and hasattr(ip, 'kernel') and hasattr(ip.kernel, '_parent_header'):
|
||||
self.msg_id = ip.kernel._parent_header['header']['msg_id']
|
||||
self.__counter += 1
|
||||
|
||||
def __exit__(self, etype, evalue, tb):
|
||||
"""Called upon exiting output widget context manager."""
|
||||
ip = get_ipython()
|
||||
if etype is not None:
|
||||
if ip:
|
||||
ip.showtraceback((etype, evalue, tb), tb_offset=0)
|
||||
self._flush()
|
||||
self.__counter -= 1
|
||||
if self.__counter == 0:
|
||||
self.msg_id = ''
|
||||
# suppress exceptions when in IPython, since they are shown above,
|
||||
# otherwise let someone else handle it
|
||||
return True if ip else None
|
||||
|
||||
def _flush(self):
|
||||
"""Flush stdout and stderr buffers."""
|
||||
sys.stdout.flush()
|
||||
sys.stderr.flush()
|
||||
|
||||
def _append_stream_output(self, text, stream_name):
|
||||
"""Append a stream output."""
|
||||
self.outputs += (
|
||||
{'output_type': 'stream', 'name': stream_name, 'text': text},
|
||||
)
|
||||
|
||||
def append_stdout(self, text):
|
||||
"""Append text to the stdout stream."""
|
||||
self._append_stream_output(text, stream_name='stdout')
|
||||
|
||||
def append_stderr(self, text):
|
||||
"""Append text to the stderr stream."""
|
||||
self._append_stream_output(text, stream_name='stderr')
|
||||
|
||||
def append_display_data(self, display_object):
|
||||
"""Append a display object as an output.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
display_object : IPython.core.display.DisplayObject
|
||||
The object to display (e.g., an instance of
|
||||
`IPython.display.Markdown` or `IPython.display.Image`).
|
||||
"""
|
||||
fmt = InteractiveShell.instance().display_formatter.format
|
||||
data, metadata = fmt(display_object)
|
||||
self.outputs += (
|
||||
{
|
||||
'output_type': 'display_data',
|
||||
'data': data,
|
||||
'metadata': metadata
|
||||
},
|
||||
)
|
637
venv/Lib/site-packages/ipywidgets/widgets/widget_selection.py
Normal file
637
venv/Lib/site-packages/ipywidgets/widgets/widget_selection.py
Normal file
|
@ -0,0 +1,637 @@
|
|||
# Copyright (c) Jupyter Development Team.
|
||||
# Distributed under the terms of the Modified BSD License.
|
||||
|
||||
"""Selection classes.
|
||||
|
||||
Represents an enumeration using a widget.
|
||||
"""
|
||||
|
||||
try:
|
||||
from collections.abc import Iterable, Mapping
|
||||
except ImportError:
|
||||
from collections import Iterable, Mapping # py2
|
||||
|
||||
try:
|
||||
from itertools import izip
|
||||
except ImportError: #python3.x
|
||||
izip = zip
|
||||
from itertools import chain
|
||||
|
||||
from .widget_description import DescriptionWidget, DescriptionStyle
|
||||
from .valuewidget import ValueWidget
|
||||
from .widget_core import CoreWidget
|
||||
from .widget_style import Style
|
||||
from .trait_types import InstanceDict, TypedTuple
|
||||
from .widget import register, widget_serialization
|
||||
from .docutils import doc_subst
|
||||
from traitlets import (Unicode, Bool, Int, Any, Dict, TraitError, CaselessStrEnum,
|
||||
Tuple, Union, observe, validate)
|
||||
from ipython_genutils.py3compat import unicode_type
|
||||
|
||||
_doc_snippets = {}
|
||||
_doc_snippets['selection_params'] = """
|
||||
options: list
|
||||
The options for the dropdown. This can either be a list of values, e.g.
|
||||
``['Galileo', 'Brahe', 'Hubble']`` or ``[0, 1, 2]``, or a list of
|
||||
(label, value) pairs, e.g.
|
||||
``[('Galileo', 0), ('Brahe', 1), ('Hubble', 2)]``.
|
||||
|
||||
index: int
|
||||
The index of the current selection.
|
||||
|
||||
value: any
|
||||
The value of the current selection. When programmatically setting the
|
||||
value, a reverse lookup is performed among the options to check that
|
||||
the value is valid. The reverse lookup uses the equality operator by
|
||||
default, but another predicate may be provided via the ``equals``
|
||||
keyword argument. For example, when dealing with numpy arrays, one may
|
||||
set ``equals=np.array_equal``.
|
||||
|
||||
label: str
|
||||
The label corresponding to the selected value.
|
||||
|
||||
disabled: bool
|
||||
Whether to disable user changes.
|
||||
|
||||
description: str
|
||||
Label for this input group. This should be a string
|
||||
describing the widget.
|
||||
"""
|
||||
|
||||
_doc_snippets['multiple_selection_params'] = """
|
||||
options: dict or list
|
||||
The options for the dropdown. This can either be a list of values, e.g.
|
||||
``['Galileo', 'Brahe', 'Hubble']`` or ``[0, 1, 2]``, a list of
|
||||
(label, value) pairs, e.g.
|
||||
``[('Galileo', 0), ('Brahe', 1), ('Hubble', 2)]``,
|
||||
or a dictionary mapping the labels to the values, e.g. ``{'Galileo': 0,
|
||||
'Brahe': 1, 'Hubble': 2}``. The labels are the strings that will be
|
||||
displayed in the UI, representing the actual Python choices, and should
|
||||
be unique. If this is a dictionary, the order in which they are
|
||||
displayed is not guaranteed.
|
||||
|
||||
index: iterable of int
|
||||
The indices of the options that are selected.
|
||||
|
||||
value: iterable
|
||||
The values that are selected. When programmatically setting the
|
||||
value, a reverse lookup is performed among the options to check that
|
||||
the value is valid. The reverse lookup uses the equality operator by
|
||||
default, but another predicate may be provided via the ``equals``
|
||||
keyword argument. For example, when dealing with numpy arrays, one may
|
||||
set ``equals=np.array_equal``.
|
||||
|
||||
label: iterable of str
|
||||
The labels corresponding to the selected value.
|
||||
|
||||
disabled: bool
|
||||
Whether to disable user changes.
|
||||
|
||||
description: str
|
||||
Label for this input group. This should be a string
|
||||
describing the widget.
|
||||
"""
|
||||
|
||||
_doc_snippets['slider_params'] = """
|
||||
orientation: str
|
||||
Either ``'horizontal'`` or ``'vertical'``. Defaults to ``horizontal``.
|
||||
|
||||
readout: bool
|
||||
Display the current label next to the slider. Defaults to ``True``.
|
||||
|
||||
continuous_update: bool
|
||||
If ``True``, update the value of the widget continuously as the user
|
||||
holds the slider. Otherwise, the model is only updated after the
|
||||
user has released the slider. Defaults to ``True``.
|
||||
"""
|
||||
|
||||
|
||||
def _make_options(x):
|
||||
"""Standardize the options tuple format.
|
||||
|
||||
The returned tuple should be in the format (('label', value), ('label', value), ...).
|
||||
|
||||
The input can be
|
||||
* an iterable of (label, value) pairs
|
||||
* an iterable of values, and labels will be generated
|
||||
"""
|
||||
# Check if x is a mapping of labels to values
|
||||
if isinstance(x, Mapping):
|
||||
import warnings
|
||||
warnings.warn("Support for mapping types has been deprecated and will be dropped in a future release.", DeprecationWarning)
|
||||
return tuple((unicode_type(k), v) for k, v in x.items())
|
||||
|
||||
# only iterate once through the options.
|
||||
xlist = tuple(x)
|
||||
|
||||
# Check if x is an iterable of (label, value) pairs
|
||||
if all((isinstance(i, (list, tuple)) and len(i) == 2) for i in xlist):
|
||||
return tuple((unicode_type(k), v) for k, v in xlist)
|
||||
|
||||
# Otherwise, assume x is an iterable of values
|
||||
return tuple((unicode_type(i), i) for i in xlist)
|
||||
|
||||
def findvalue(array, value, compare = lambda x, y: x == y):
|
||||
"A function that uses the compare function to return a value from the list."
|
||||
try:
|
||||
return next(x for x in array if compare(x, value))
|
||||
except StopIteration:
|
||||
raise ValueError('%r not in array'%value)
|
||||
|
||||
class _Selection(DescriptionWidget, ValueWidget, CoreWidget):
|
||||
"""Base class for Selection widgets
|
||||
|
||||
``options`` can be specified as a list of values, list of (label, value)
|
||||
tuples, or a dict of {label: value}. The labels are the strings that will be
|
||||
displayed in the UI, representing the actual Python choices, and should be
|
||||
unique. If labels are not specified, they are generated from the values.
|
||||
|
||||
When programmatically setting the value, a reverse lookup is performed
|
||||
among the options to check that the value is valid. The reverse lookup uses
|
||||
the equality operator by default, but another predicate may be provided via
|
||||
the ``equals`` keyword argument. For example, when dealing with numpy arrays,
|
||||
one may set equals=np.array_equal.
|
||||
"""
|
||||
|
||||
value = Any(None, help="Selected value", allow_none=True)
|
||||
label = Unicode(None, help="Selected label", allow_none=True)
|
||||
index = Int(None, help="Selected index", allow_none=True).tag(sync=True)
|
||||
|
||||
options = Any((),
|
||||
help="""Iterable of values, (label, value) pairs, or a mapping of {label: value} pairs that the user can select.
|
||||
|
||||
The labels are the strings that will be displayed in the UI, representing the
|
||||
actual Python choices, and should be unique.
|
||||
""")
|
||||
|
||||
_options_full = None
|
||||
|
||||
# This being read-only means that it cannot be changed by the user.
|
||||
_options_labels = TypedTuple(trait=Unicode(), read_only=True, help="The labels for the options.").tag(sync=True)
|
||||
|
||||
disabled = Bool(help="Enable or disable user changes").tag(sync=True)
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.equals = kwargs.pop('equals', lambda x, y: x == y)
|
||||
# We have to make the basic options bookkeeping consistent
|
||||
# so we don't have errors the first time validators run
|
||||
self._initializing_traits_ = True
|
||||
options = _make_options(kwargs.get('options', ()))
|
||||
self._options_full = options
|
||||
self.set_trait('_options_labels', tuple(i[0] for i in options))
|
||||
self._options_values = tuple(i[1] for i in options)
|
||||
|
||||
# Select the first item by default, if we can
|
||||
if 'index' not in kwargs and 'value' not in kwargs and 'label' not in kwargs:
|
||||
nonempty = (len(options) > 0)
|
||||
kwargs['index'] = 0 if nonempty else None
|
||||
kwargs['label'], kwargs['value'] = options[0] if nonempty else (None, None)
|
||||
|
||||
super(_Selection, self).__init__(*args, **kwargs)
|
||||
self._initializing_traits_ = False
|
||||
|
||||
@validate('options')
|
||||
def _validate_options(self, proposal):
|
||||
# if an iterator is provided, exhaust it
|
||||
if isinstance(proposal.value, Iterable) and not isinstance(proposal.value, Mapping):
|
||||
proposal.value = tuple(proposal.value)
|
||||
# throws an error if there is a problem converting to full form
|
||||
self._options_full = _make_options(proposal.value)
|
||||
return proposal.value
|
||||
|
||||
@observe('options')
|
||||
def _propagate_options(self, change):
|
||||
"Set the values and labels, and select the first option if we aren't initializing"
|
||||
options = self._options_full
|
||||
self.set_trait('_options_labels', tuple(i[0] for i in options))
|
||||
self._options_values = tuple(i[1] for i in options)
|
||||
if self._initializing_traits_ is not True:
|
||||
if len(options) > 0:
|
||||
if self.index == 0:
|
||||
# Explicitly trigger the observers to pick up the new value and
|
||||
# label. Just setting the value would not trigger the observers
|
||||
# since traitlets thinks the value hasn't changed.
|
||||
self._notify_trait('index', 0, 0)
|
||||
else:
|
||||
self.index = 0
|
||||
else:
|
||||
self.index = None
|
||||
|
||||
@validate('index')
|
||||
def _validate_index(self, proposal):
|
||||
if proposal.value is None or 0 <= proposal.value < len(self._options_labels):
|
||||
return proposal.value
|
||||
else:
|
||||
raise TraitError('Invalid selection: index out of bounds')
|
||||
|
||||
@observe('index')
|
||||
def _propagate_index(self, change):
|
||||
"Propagate changes in index to the value and label properties"
|
||||
label = self._options_labels[change.new] if change.new is not None else None
|
||||
value = self._options_values[change.new] if change.new is not None else None
|
||||
if self.label is not label:
|
||||
self.label = label
|
||||
if self.value is not value:
|
||||
self.value = value
|
||||
|
||||
@validate('value')
|
||||
def _validate_value(self, proposal):
|
||||
value = proposal.value
|
||||
try:
|
||||
return findvalue(self._options_values, value, self.equals) if value is not None else None
|
||||
except ValueError:
|
||||
raise TraitError('Invalid selection: value not found')
|
||||
|
||||
@observe('value')
|
||||
def _propagate_value(self, change):
|
||||
if change.new is None:
|
||||
index = None
|
||||
elif self.index is not None and self._options_values[self.index] == change.new:
|
||||
index = self.index
|
||||
else:
|
||||
index = self._options_values.index(change.new)
|
||||
if self.index != index:
|
||||
self.index = index
|
||||
|
||||
@validate('label')
|
||||
def _validate_label(self, proposal):
|
||||
if (proposal.value is not None) and (proposal.value not in self._options_labels):
|
||||
raise TraitError('Invalid selection: label not found')
|
||||
return proposal.value
|
||||
|
||||
@observe('label')
|
||||
def _propagate_label(self, change):
|
||||
if change.new is None:
|
||||
index = None
|
||||
elif self.index is not None and self._options_labels[self.index] == change.new:
|
||||
index = self.index
|
||||
else:
|
||||
index = self._options_labels.index(change.new)
|
||||
if self.index != index:
|
||||
self.index = index
|
||||
|
||||
def _repr_keys(self):
|
||||
keys = super(_Selection, self)._repr_keys()
|
||||
# Include options manually, as it isn't marked as synced:
|
||||
for key in sorted(chain(keys, ('options',))):
|
||||
if key == 'index' and self.index == 0:
|
||||
# Index 0 is default when there are options
|
||||
continue
|
||||
yield key
|
||||
|
||||
|
||||
class _MultipleSelection(DescriptionWidget, ValueWidget, CoreWidget):
|
||||
"""Base class for multiple Selection widgets
|
||||
|
||||
``options`` can be specified as a list of values, list of (label, value)
|
||||
tuples, or a dict of {label: value}. The labels are the strings that will be
|
||||
displayed in the UI, representing the actual Python choices, and should be
|
||||
unique. If labels are not specified, they are generated from the values.
|
||||
|
||||
When programmatically setting the value, a reverse lookup is performed
|
||||
among the options to check that the value is valid. The reverse lookup uses
|
||||
the equality operator by default, but another predicate may be provided via
|
||||
the ``equals`` keyword argument. For example, when dealing with numpy arrays,
|
||||
one may set equals=np.array_equal.
|
||||
"""
|
||||
|
||||
value = TypedTuple(trait=Any(), help="Selected values")
|
||||
label = TypedTuple(trait=Unicode(), help="Selected labels")
|
||||
index = TypedTuple(trait=Int(), help="Selected indices").tag(sync=True)
|
||||
|
||||
options = Any((),
|
||||
help="""Iterable of values, (label, value) pairs, or a mapping of {label: value} pairs that the user can select.
|
||||
|
||||
The labels are the strings that will be displayed in the UI, representing the
|
||||
actual Python choices, and should be unique.
|
||||
""")
|
||||
_options_full = None
|
||||
|
||||
# This being read-only means that it cannot be changed from the frontend!
|
||||
_options_labels = TypedTuple(trait=Unicode(), read_only=True, help="The labels for the options.").tag(sync=True)
|
||||
|
||||
disabled = Bool(help="Enable or disable user changes").tag(sync=True)
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.equals = kwargs.pop('equals', lambda x, y: x == y)
|
||||
|
||||
# We have to make the basic options bookkeeping consistent
|
||||
# so we don't have errors the first time validators run
|
||||
self._initializing_traits_ = True
|
||||
options = _make_options(kwargs.get('options', ()))
|
||||
self._full_options = options
|
||||
self.set_trait('_options_labels', tuple(i[0] for i in options))
|
||||
self._options_values = tuple(i[1] for i in options)
|
||||
|
||||
super(_MultipleSelection, self).__init__(*args, **kwargs)
|
||||
self._initializing_traits_ = False
|
||||
|
||||
@validate('options')
|
||||
def _validate_options(self, proposal):
|
||||
if isinstance(proposal.value, Iterable) and not isinstance(proposal.value, Mapping):
|
||||
proposal.value = tuple(proposal.value)
|
||||
# throws an error if there is a problem converting to full form
|
||||
self._options_full = _make_options(proposal.value)
|
||||
return proposal.value
|
||||
|
||||
@observe('options')
|
||||
def _propagate_options(self, change):
|
||||
"Unselect any option"
|
||||
options = self._options_full
|
||||
self.set_trait('_options_labels', tuple(i[0] for i in options))
|
||||
self._options_values = tuple(i[1] for i in options)
|
||||
if self._initializing_traits_ is not True:
|
||||
self.index = ()
|
||||
|
||||
@validate('index')
|
||||
def _validate_index(self, proposal):
|
||||
"Check the range of each proposed index."
|
||||
if all(0 <= i < len(self._options_labels) for i in proposal.value):
|
||||
return proposal.value
|
||||
else:
|
||||
raise TraitError('Invalid selection: index out of bounds')
|
||||
|
||||
@observe('index')
|
||||
def _propagate_index(self, change):
|
||||
"Propagate changes in index to the value and label properties"
|
||||
label = tuple(self._options_labels[i] for i in change.new)
|
||||
value = tuple(self._options_values[i] for i in change.new)
|
||||
# we check equality so we can avoid validation if possible
|
||||
if self.label != label:
|
||||
self.label = label
|
||||
if self.value != value:
|
||||
self.value = value
|
||||
|
||||
@validate('value')
|
||||
def _validate_value(self, proposal):
|
||||
"Replace all values with the actual objects in the options list"
|
||||
try:
|
||||
return tuple(findvalue(self._options_values, i, self.equals) for i in proposal.value)
|
||||
except ValueError:
|
||||
raise TraitError('Invalid selection: value not found')
|
||||
|
||||
@observe('value')
|
||||
def _propagate_value(self, change):
|
||||
index = tuple(self._options_values.index(i) for i in change.new)
|
||||
if self.index != index:
|
||||
self.index = index
|
||||
|
||||
@validate('label')
|
||||
def _validate_label(self, proposal):
|
||||
if any(i not in self._options_labels for i in proposal.value):
|
||||
raise TraitError('Invalid selection: label not found')
|
||||
return proposal.value
|
||||
|
||||
@observe('label')
|
||||
def _propagate_label(self, change):
|
||||
index = tuple(self._options_labels.index(i) for i in change.new)
|
||||
if self.index != index:
|
||||
self.index = index
|
||||
|
||||
def _repr_keys(self):
|
||||
keys = super(_MultipleSelection, self)._repr_keys()
|
||||
# Include options manually, as it isn't marked as synced:
|
||||
for key in sorted(chain(keys, ('options',))):
|
||||
yield key
|
||||
|
||||
|
||||
@register
|
||||
class ToggleButtonsStyle(DescriptionStyle, CoreWidget):
|
||||
"""Button style widget.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
button_width: str
|
||||
The width of each button. This should be a valid CSS
|
||||
width, e.g. '10px' or '5em'.
|
||||
|
||||
font_weight: str
|
||||
The text font weight of each button, This should be a valid CSS font
|
||||
weight unit, for example 'bold' or '600'
|
||||
"""
|
||||
_model_name = Unicode('ToggleButtonsStyleModel').tag(sync=True)
|
||||
button_width = Unicode(help="The width of each button.").tag(sync=True)
|
||||
font_weight = Unicode(help="Text font weight of each button.").tag(sync=True)
|
||||
|
||||
|
||||
@register
|
||||
@doc_subst(_doc_snippets)
|
||||
class ToggleButtons(_Selection):
|
||||
"""Group of toggle buttons that represent an enumeration.
|
||||
|
||||
Only one toggle button can be toggled at any point in time.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
{selection_params}
|
||||
|
||||
tooltips: list
|
||||
Tooltip for each button. If specified, must be the
|
||||
same length as `options`.
|
||||
|
||||
icons: list
|
||||
Icons to show on the buttons. This must be the name
|
||||
of a font-awesome icon. See `http://fontawesome.io/icons/`
|
||||
for a list of icons.
|
||||
|
||||
button_style: str
|
||||
One of 'primary', 'success', 'info', 'warning' or
|
||||
'danger'. Applies a predefined style to every button.
|
||||
|
||||
style: ToggleButtonsStyle
|
||||
Style parameters for the buttons.
|
||||
"""
|
||||
_view_name = Unicode('ToggleButtonsView').tag(sync=True)
|
||||
_model_name = Unicode('ToggleButtonsModel').tag(sync=True)
|
||||
|
||||
tooltips = TypedTuple(Unicode(), help="Tooltips for each button.").tag(sync=True)
|
||||
icons = TypedTuple(Unicode(), help="Icons names for each button (FontAwesome names without the fa- prefix).").tag(sync=True)
|
||||
style = InstanceDict(ToggleButtonsStyle).tag(sync=True, **widget_serialization)
|
||||
|
||||
button_style = CaselessStrEnum(
|
||||
values=['primary', 'success', 'info', 'warning', 'danger', ''],
|
||||
default_value='', allow_none=True, help="""Use a predefined styling for the buttons.""").tag(sync=True)
|
||||
|
||||
|
||||
@register
|
||||
@doc_subst(_doc_snippets)
|
||||
class Dropdown(_Selection):
|
||||
"""Allows you to select a single item from a dropdown.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
{selection_params}
|
||||
"""
|
||||
_view_name = Unicode('DropdownView').tag(sync=True)
|
||||
_model_name = Unicode('DropdownModel').tag(sync=True)
|
||||
|
||||
|
||||
@register
|
||||
@doc_subst(_doc_snippets)
|
||||
class RadioButtons(_Selection):
|
||||
"""Group of radio buttons that represent an enumeration.
|
||||
|
||||
Only one radio button can be toggled at any point in time.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
{selection_params}
|
||||
"""
|
||||
_view_name = Unicode('RadioButtonsView').tag(sync=True)
|
||||
_model_name = Unicode('RadioButtonsModel').tag(sync=True)
|
||||
|
||||
|
||||
@register
|
||||
@doc_subst(_doc_snippets)
|
||||
class Select(_Selection):
|
||||
"""
|
||||
Listbox that only allows one item to be selected at any given time.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
{selection_params}
|
||||
|
||||
rows: int
|
||||
The number of rows to display in the widget.
|
||||
"""
|
||||
_view_name = Unicode('SelectView').tag(sync=True)
|
||||
_model_name = Unicode('SelectModel').tag(sync=True)
|
||||
rows = Int(5, help="The number of rows to display.").tag(sync=True)
|
||||
|
||||
@register
|
||||
@doc_subst(_doc_snippets)
|
||||
class SelectMultiple(_MultipleSelection):
|
||||
"""
|
||||
Listbox that allows many items to be selected at any given time.
|
||||
|
||||
The ``value``, ``label`` and ``index`` attributes are all iterables.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
{multiple_selection_params}
|
||||
|
||||
rows: int
|
||||
The number of rows to display in the widget.
|
||||
"""
|
||||
_view_name = Unicode('SelectMultipleView').tag(sync=True)
|
||||
_model_name = Unicode('SelectMultipleModel').tag(sync=True)
|
||||
rows = Int(5, help="The number of rows to display.").tag(sync=True)
|
||||
|
||||
|
||||
class _SelectionNonempty(_Selection):
|
||||
"""Selection that is guaranteed to have a value selected."""
|
||||
# don't allow None to be an option.
|
||||
value = Any(help="Selected value")
|
||||
label = Unicode(help="Selected label")
|
||||
index = Int(help="Selected index").tag(sync=True)
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
if len(kwargs.get('options', ())) == 0:
|
||||
raise TraitError('options must be nonempty')
|
||||
super(_SelectionNonempty, self).__init__(*args, **kwargs)
|
||||
|
||||
@validate('options')
|
||||
def _validate_options(self, proposal):
|
||||
if isinstance(proposal.value, Iterable) and not isinstance(proposal.value, Mapping):
|
||||
proposal.value = tuple(proposal.value)
|
||||
self._options_full = _make_options(proposal.value)
|
||||
if len(self._options_full) == 0:
|
||||
raise TraitError("Option list must be nonempty")
|
||||
return proposal.value
|
||||
|
||||
@validate('index')
|
||||
def _validate_index(self, proposal):
|
||||
if 0 <= proposal.value < len(self._options_labels):
|
||||
return proposal.value
|
||||
else:
|
||||
raise TraitError('Invalid selection: index out of bounds')
|
||||
|
||||
class _MultipleSelectionNonempty(_MultipleSelection):
|
||||
"""Selection that is guaranteed to have an option available."""
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
if len(kwargs.get('options', ())) == 0:
|
||||
raise TraitError('options must be nonempty')
|
||||
super(_MultipleSelectionNonempty, self).__init__(*args, **kwargs)
|
||||
|
||||
@validate('options')
|
||||
def _validate_options(self, proposal):
|
||||
if isinstance(proposal.value, Iterable) and not isinstance(proposal.value, Mapping):
|
||||
proposal.value = tuple(proposal.value)
|
||||
# throws an error if there is a problem converting to full form
|
||||
self._options_full = _make_options(proposal.value)
|
||||
if len(self._options_full) == 0:
|
||||
raise TraitError("Option list must be nonempty")
|
||||
return proposal.value
|
||||
|
||||
@register
|
||||
@doc_subst(_doc_snippets)
|
||||
class SelectionSlider(_SelectionNonempty):
|
||||
"""
|
||||
Slider to select a single item from a list or dictionary.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
{selection_params}
|
||||
|
||||
{slider_params}
|
||||
"""
|
||||
_view_name = Unicode('SelectionSliderView').tag(sync=True)
|
||||
_model_name = Unicode('SelectionSliderModel').tag(sync=True)
|
||||
|
||||
orientation = CaselessStrEnum(
|
||||
values=['horizontal', 'vertical'], default_value='horizontal',
|
||||
help="Vertical or horizontal.").tag(sync=True)
|
||||
readout = Bool(True,
|
||||
help="Display the current selected label next to the slider").tag(sync=True)
|
||||
continuous_update = Bool(True,
|
||||
help="Update the value of the widget as the user is holding the slider.").tag(sync=True)
|
||||
|
||||
@register
|
||||
@doc_subst(_doc_snippets)
|
||||
class SelectionRangeSlider(_MultipleSelectionNonempty):
|
||||
"""
|
||||
Slider to select multiple contiguous items from a list.
|
||||
|
||||
The index, value, and label attributes contain the start and end of
|
||||
the selection range, not all items in the range.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
{multiple_selection_params}
|
||||
|
||||
{slider_params}
|
||||
"""
|
||||
_view_name = Unicode('SelectionRangeSliderView').tag(sync=True)
|
||||
_model_name = Unicode('SelectionRangeSliderModel').tag(sync=True)
|
||||
|
||||
value = Tuple(help="Min and max selected values")
|
||||
label = Tuple(help="Min and max selected labels")
|
||||
index = Tuple((0,0), help="Min and max selected indices").tag(sync=True)
|
||||
|
||||
@observe('options')
|
||||
def _propagate_options(self, change):
|
||||
"Select the first range"
|
||||
options = self._options_full
|
||||
self.set_trait('_options_labels', tuple(i[0] for i in options))
|
||||
self._options_values = tuple(i[1] for i in options)
|
||||
if self._initializing_traits_ is not True:
|
||||
self.index = (0, 0)
|
||||
|
||||
@validate('index')
|
||||
def _validate_index(self, proposal):
|
||||
"Make sure we have two indices and check the range of each proposed index."
|
||||
if len(proposal.value) != 2:
|
||||
raise TraitError('Invalid selection: index must have two values, but is %r'%(proposal.value,))
|
||||
if all(0 <= i < len(self._options_labels) for i in proposal.value):
|
||||
return proposal.value
|
||||
else:
|
||||
raise TraitError('Invalid selection: index out of bounds: %s'%(proposal.value,))
|
||||
|
||||
orientation = CaselessStrEnum(
|
||||
values=['horizontal', 'vertical'], default_value='horizontal',
|
||||
help="Vertical or horizontal.").tag(sync=True)
|
||||
readout = Bool(True,
|
||||
help="Display the current selected label next to the slider").tag(sync=True)
|
||||
continuous_update = Bool(True,
|
||||
help="Update the value of the widget as the user is holding the slider.").tag(sync=True)
|
|
@ -0,0 +1,82 @@
|
|||
# Copyright (c) Jupyter Development Team.
|
||||
# Distributed under the terms of the Modified BSD License.
|
||||
|
||||
"""SelectionContainer class.
|
||||
|
||||
Represents a multipage container that can be used to group other widgets into
|
||||
pages.
|
||||
"""
|
||||
|
||||
from .widget_box import Box
|
||||
from .widget import register
|
||||
from .widget_core import CoreWidget
|
||||
from traitlets import Unicode, Dict, CInt, TraitError, validate
|
||||
from ipython_genutils.py3compat import unicode_type
|
||||
|
||||
|
||||
class _SelectionContainer(Box, CoreWidget):
|
||||
"""Base class used to display multiple child widgets."""
|
||||
_titles = Dict(help="Titles of the pages").tag(sync=True)
|
||||
selected_index = CInt(
|
||||
help="""The index of the selected page. This is either an integer selecting a particular sub-widget, or None to have no widgets selected.""",
|
||||
allow_none=True
|
||||
).tag(sync=True)
|
||||
|
||||
@validate('selected_index')
|
||||
def _validated_index(self, proposal):
|
||||
if proposal.value is None or 0 <= proposal.value < len(self.children):
|
||||
return proposal.value
|
||||
else:
|
||||
raise TraitError('Invalid selection: index out of bounds')
|
||||
|
||||
# Public methods
|
||||
def set_title(self, index, title):
|
||||
"""Sets the title of a container page.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
index : int
|
||||
Index of the container page
|
||||
title : unicode
|
||||
New title
|
||||
"""
|
||||
# JSON dictionaries have string keys, so we convert index to a string
|
||||
index = unicode_type(int(index))
|
||||
self._titles[index] = title
|
||||
self.send_state('_titles')
|
||||
|
||||
def get_title(self, index):
|
||||
"""Gets the title of a container pages.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
index : int
|
||||
Index of the container page
|
||||
"""
|
||||
# JSON dictionaries have string keys, so we convert index to a string
|
||||
index = unicode_type(int(index))
|
||||
if index in self._titles:
|
||||
return self._titles[index]
|
||||
else:
|
||||
return None
|
||||
|
||||
def _repr_keys(self):
|
||||
# We also need to include _titles in repr for reproducibility
|
||||
for key in super(_SelectionContainer, self)._repr_keys():
|
||||
yield key
|
||||
if self._titles:
|
||||
yield '_titles'
|
||||
|
||||
|
||||
@register
|
||||
class Accordion(_SelectionContainer):
|
||||
"""Displays children each on a separate accordion page."""
|
||||
_view_name = Unicode('AccordionView').tag(sync=True)
|
||||
_model_name = Unicode('AccordionModel').tag(sync=True)
|
||||
|
||||
|
||||
@register
|
||||
class Tab(_SelectionContainer):
|
||||
"""Displays children each on a separate accordion tab."""
|
||||
_view_name = Unicode('TabView').tag(sync=True)
|
||||
_model_name = Unicode('TabModel').tag(sync=True)
|
141
venv/Lib/site-packages/ipywidgets/widgets/widget_string.py
Normal file
141
venv/Lib/site-packages/ipywidgets/widgets/widget_string.py
Normal file
|
@ -0,0 +1,141 @@
|
|||
# Copyright (c) Jupyter Development Team.
|
||||
# Distributed under the terms of the Modified BSD License.
|
||||
|
||||
"""String class.
|
||||
|
||||
Represents a unicode string using a widget.
|
||||
"""
|
||||
|
||||
from .widget_description import DescriptionWidget
|
||||
from .valuewidget import ValueWidget
|
||||
from .widget import CallbackDispatcher, register
|
||||
from .widget_core import CoreWidget
|
||||
from .trait_types import TypedTuple
|
||||
from traitlets import Unicode, Bool, Int
|
||||
from warnings import warn
|
||||
|
||||
|
||||
class _String(DescriptionWidget, ValueWidget, CoreWidget):
|
||||
"""Base class used to create widgets that represent a string."""
|
||||
|
||||
value = Unicode(help="String value").tag(sync=True)
|
||||
|
||||
# We set a zero-width space as a default placeholder to make sure the baseline matches
|
||||
# the text, not the bottom margin. See the last paragraph of
|
||||
# https://www.w3.org/TR/CSS2/visudet.html#leading
|
||||
placeholder = Unicode(u'\u200b', help="Placeholder text to display when nothing has been typed").tag(sync=True)
|
||||
|
||||
|
||||
def __init__(self, value=None, **kwargs):
|
||||
if value is not None:
|
||||
kwargs['value'] = value
|
||||
super(_String, self).__init__(**kwargs)
|
||||
|
||||
_model_name = Unicode('StringModel').tag(sync=True)
|
||||
|
||||
|
||||
@register
|
||||
class HTML(_String):
|
||||
"""Renders the string `value` as HTML."""
|
||||
_view_name = Unicode('HTMLView').tag(sync=True)
|
||||
_model_name = Unicode('HTMLModel').tag(sync=True)
|
||||
|
||||
@register
|
||||
class HTMLMath(_String):
|
||||
"""Renders the string `value` as HTML, and render mathematics."""
|
||||
_view_name = Unicode('HTMLMathView').tag(sync=True)
|
||||
_model_name = Unicode('HTMLMathModel').tag(sync=True)
|
||||
|
||||
|
||||
@register
|
||||
class Label(_String):
|
||||
"""Label widget.
|
||||
|
||||
It also renders math inside the string `value` as Latex (requires $ $ or
|
||||
$$ $$ and similar latex tags).
|
||||
"""
|
||||
_view_name = Unicode('LabelView').tag(sync=True)
|
||||
_model_name = Unicode('LabelModel').tag(sync=True)
|
||||
|
||||
|
||||
@register
|
||||
class Textarea(_String):
|
||||
"""Multiline text area widget."""
|
||||
_view_name = Unicode('TextareaView').tag(sync=True)
|
||||
_model_name = Unicode('TextareaModel').tag(sync=True)
|
||||
rows = Int(None, allow_none=True, help="The number of rows to display.").tag(sync=True)
|
||||
disabled = Bool(False, help="Enable or disable user changes").tag(sync=True)
|
||||
continuous_update = Bool(True, help="Update the value as the user types. If False, update on submission, e.g., pressing Enter or navigating away.").tag(sync=True)
|
||||
|
||||
@register
|
||||
class Text(_String):
|
||||
"""Single line textbox widget."""
|
||||
_view_name = Unicode('TextView').tag(sync=True)
|
||||
_model_name = Unicode('TextModel').tag(sync=True)
|
||||
disabled = Bool(False, help="Enable or disable user changes").tag(sync=True)
|
||||
continuous_update = Bool(True, help="Update the value as the user types. If False, update on submission, e.g., pressing Enter or navigating away.").tag(sync=True)
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(Text, self).__init__(*args, **kwargs)
|
||||
self._submission_callbacks = CallbackDispatcher()
|
||||
self.on_msg(self._handle_string_msg)
|
||||
|
||||
def _handle_string_msg(self, _, content, buffers):
|
||||
"""Handle a msg from the front-end.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
content: dict
|
||||
Content of the msg.
|
||||
"""
|
||||
if content.get('event', '') == 'submit':
|
||||
self._submission_callbacks(self)
|
||||
|
||||
def on_submit(self, callback, remove=False):
|
||||
"""(Un)Register a callback to handle text submission.
|
||||
|
||||
Triggered when the user clicks enter.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
callback: callable
|
||||
Will be called with exactly one argument: the Widget instance
|
||||
remove: bool (optional)
|
||||
Whether to unregister the callback
|
||||
"""
|
||||
import warnings
|
||||
warnings.warn("on_submit is deprecated. Instead, set the .continuous_update attribute to False and observe the value changing with: mywidget.observe(callback, 'value').", DeprecationWarning)
|
||||
self._submission_callbacks.register_callback(callback, remove=remove)
|
||||
|
||||
|
||||
@register
|
||||
class Password(Text):
|
||||
"""Single line textbox widget."""
|
||||
_view_name = Unicode('PasswordView').tag(sync=True)
|
||||
_model_name = Unicode('PasswordModel').tag(sync=True)
|
||||
disabled = Bool(False, help="Enable or disable user changes").tag(sync=True)
|
||||
|
||||
def _repr_keys(self):
|
||||
# Don't include password value in repr!
|
||||
super_keys = super(Password, self)._repr_keys()
|
||||
for key in super_keys:
|
||||
if key != 'value':
|
||||
yield key
|
||||
|
||||
|
||||
@register
|
||||
class Combobox(Text):
|
||||
"""Single line textbox widget with a dropdown and autocompletion.
|
||||
"""
|
||||
_model_name = Unicode('ComboboxModel').tag(sync=True)
|
||||
_view_name = Unicode('ComboboxView').tag(sync=True)
|
||||
|
||||
options = TypedTuple(
|
||||
trait=Unicode(),
|
||||
help="Dropdown options for the combobox"
|
||||
).tag(sync=True)
|
||||
|
||||
ensure_option = Bool(
|
||||
False,
|
||||
help='If set, ensure value is in options. Implies continuous_update=False.'
|
||||
).tag(sync=True)
|
16
venv/Lib/site-packages/ipywidgets/widgets/widget_style.py
Normal file
16
venv/Lib/site-packages/ipywidgets/widgets/widget_style.py
Normal file
|
@ -0,0 +1,16 @@
|
|||
# Copyright (c) Jupyter Development Team.
|
||||
# Distributed under the terms of the Modified BSD License.
|
||||
|
||||
"""Contains the Style class"""
|
||||
|
||||
from traitlets import Unicode
|
||||
from .widget import Widget
|
||||
from .._version import __jupyter_widgets_base_version__
|
||||
|
||||
class Style(Widget):
|
||||
"""Style specification"""
|
||||
|
||||
_model_name = Unicode('StyleModel').tag(sync=True)
|
||||
_view_name = Unicode('StyleView').tag(sync=True)
|
||||
_view_module = Unicode('@jupyter-widgets/base').tag(sync=True)
|
||||
_view_module_version = Unicode(__jupyter_widgets_base_version__).tag(sync=True)
|
454
venv/Lib/site-packages/ipywidgets/widgets/widget_templates.py
Normal file
454
venv/Lib/site-packages/ipywidgets/widgets/widget_templates.py
Normal file
|
@ -0,0 +1,454 @@
|
|||
"""Implement common widgets layouts as reusable components"""
|
||||
|
||||
import re
|
||||
from collections import defaultdict
|
||||
|
||||
from traitlets import Instance, Bool, Unicode, CUnicode, CaselessStrEnum, Tuple
|
||||
from traitlets import Integer
|
||||
from traitlets import HasTraits, TraitError
|
||||
from traitlets import observe, validate
|
||||
|
||||
from .widget import Widget
|
||||
from .widget_box import GridBox
|
||||
|
||||
from .docutils import doc_subst
|
||||
|
||||
|
||||
_doc_snippets = {
|
||||
'style_params' : """
|
||||
|
||||
grid_gap : str
|
||||
CSS attribute used to set the gap between the grid cells
|
||||
|
||||
justify_content : str, in ['flex-start', 'flex-end', 'center', 'space-between', 'space-around']
|
||||
CSS attribute used to align widgets vertically
|
||||
|
||||
align_items : str, in ['top', 'bottom', 'center', 'flex-start', 'flex-end', 'baseline', 'stretch']
|
||||
CSS attribute used to align widgets horizontally
|
||||
|
||||
width : str
|
||||
height : str
|
||||
width and height"""
|
||||
}
|
||||
|
||||
@doc_subst(_doc_snippets)
|
||||
class LayoutProperties(HasTraits):
|
||||
"""Mixin class for layout templates
|
||||
|
||||
This class handles mainly style attributes (height, grid_gap etc.)
|
||||
|
||||
Parameters
|
||||
----------
|
||||
|
||||
{style_params}
|
||||
|
||||
|
||||
Note
|
||||
----
|
||||
|
||||
This class is only meant to be used in inheritance as mixin with other
|
||||
classes. It will not work, unless `self.layout` attribute is defined.
|
||||
|
||||
"""
|
||||
|
||||
# style attributes (passed to Layout)
|
||||
grid_gap = Unicode(
|
||||
None,
|
||||
allow_none=True,
|
||||
help="The grid-gap CSS attribute.")
|
||||
justify_content = CaselessStrEnum(
|
||||
['flex-start', 'flex-end', 'center',
|
||||
'space-between', 'space-around'],
|
||||
allow_none=True,
|
||||
help="The justify-content CSS attribute.")
|
||||
align_items = CaselessStrEnum(
|
||||
['top', 'bottom',
|
||||
'flex-start', 'flex-end', 'center',
|
||||
'baseline', 'stretch'],
|
||||
allow_none=True, help="The align-items CSS attribute.")
|
||||
width = Unicode(
|
||||
None,
|
||||
allow_none=True,
|
||||
help="The width CSS attribute.")
|
||||
height = Unicode(
|
||||
None,
|
||||
allow_none=True,
|
||||
help="The width CSS attribute.")
|
||||
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
super(LayoutProperties, self).__init__(**kwargs)
|
||||
self._property_rewrite = defaultdict(dict)
|
||||
self._property_rewrite['align_items'] = {'top': 'flex-start',
|
||||
'bottom': 'flex-end'}
|
||||
self._copy_layout_props()
|
||||
self._set_observers()
|
||||
|
||||
def _delegate_to_layout(self, change):
|
||||
"delegate the trait types to their counterparts in self.layout"
|
||||
value, name = change['new'], change['name']
|
||||
value = self._property_rewrite[name].get(value, value)
|
||||
setattr(self.layout, name, value) # pylint: disable=no-member
|
||||
|
||||
def _set_observers(self):
|
||||
"set observers on all layout properties defined in this class"
|
||||
_props = LayoutProperties.class_trait_names()
|
||||
self.observe(self._delegate_to_layout, _props)
|
||||
|
||||
def _copy_layout_props(self):
|
||||
|
||||
_props = LayoutProperties.class_trait_names()
|
||||
|
||||
for prop in _props:
|
||||
value = getattr(self, prop)
|
||||
if value:
|
||||
value = self._property_rewrite[prop].get(value, value)
|
||||
setattr(self.layout, prop, value) #pylint: disable=no-member
|
||||
|
||||
@doc_subst(_doc_snippets)
|
||||
class AppLayout(GridBox, LayoutProperties):
|
||||
""" Define an application like layout of widgets.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
|
||||
header: instance of Widget
|
||||
left_sidebar: instance of Widget
|
||||
center: instance of Widget
|
||||
right_sidebar: instance of Widget
|
||||
footer: instance of Widget
|
||||
widgets to fill the positions in the layout
|
||||
|
||||
merge: bool
|
||||
flag to say whether the empty positions should be automatically merged
|
||||
|
||||
pane_widths: list of numbers/strings
|
||||
the fraction of the total layout width each of the central panes should occupy
|
||||
(left_sidebar,
|
||||
center, right_sidebar)
|
||||
|
||||
pane_heights: list of numbers/strings
|
||||
the fraction of the width the vertical space that the panes should occupy
|
||||
(left_sidebar, center, right_sidebar)
|
||||
|
||||
{style_params}
|
||||
|
||||
Examples
|
||||
--------
|
||||
|
||||
"""
|
||||
|
||||
# widget positions
|
||||
header = Instance(Widget, allow_none=True)
|
||||
footer = Instance(Widget, allow_none=True)
|
||||
left_sidebar = Instance(Widget, allow_none=True)
|
||||
right_sidebar = Instance(Widget, allow_none=True)
|
||||
center = Instance(Widget, allow_none=True)
|
||||
|
||||
# extra args
|
||||
pane_widths = Tuple(CUnicode(), CUnicode(), CUnicode(),
|
||||
default_value=['1fr', '2fr', '1fr'])
|
||||
pane_heights = Tuple(CUnicode(), CUnicode(), CUnicode(),
|
||||
default_value=['1fr', '3fr', '1fr'])
|
||||
|
||||
merge = Bool(default_value=True)
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
super(AppLayout, self).__init__(**kwargs)
|
||||
self._update_layout()
|
||||
|
||||
@staticmethod
|
||||
def _size_to_css(size):
|
||||
if re.match(r'\d+\.?\d*(px|fr|%)$', size):
|
||||
return size
|
||||
if re.match(r'\d+\.?\d*$', size):
|
||||
return size + 'fr'
|
||||
|
||||
raise TypeError("the pane sizes must be in one of the following formats: "
|
||||
"'10px', '10fr', 10 (will be converted to '10fr')."
|
||||
"Got '{}'".format(size))
|
||||
|
||||
def _convert_sizes(self, size_list):
|
||||
return list(map(self._size_to_css, size_list))
|
||||
|
||||
def _update_layout(self):
|
||||
|
||||
grid_template_areas = [["header", "header", "header"],
|
||||
["left-sidebar", "center", "right-sidebar"],
|
||||
["footer", "footer", "footer"]]
|
||||
|
||||
grid_template_columns = self._convert_sizes(self.pane_widths)
|
||||
grid_template_rows = self._convert_sizes(self.pane_heights)
|
||||
|
||||
all_children = {'header': self.header,
|
||||
'footer': self.footer,
|
||||
'left-sidebar': self.left_sidebar,
|
||||
'right-sidebar': self.right_sidebar,
|
||||
'center': self.center}
|
||||
|
||||
children = {position : child
|
||||
for position, child in all_children.items()
|
||||
if child is not None}
|
||||
|
||||
if not children:
|
||||
return
|
||||
|
||||
for position, child in children.items():
|
||||
child.layout.grid_area = position
|
||||
|
||||
if self.merge:
|
||||
|
||||
if len(children) == 1:
|
||||
position = list(children.keys())[0]
|
||||
grid_template_areas = [[position, position, position],
|
||||
[position, position, position],
|
||||
[position, position, position]]
|
||||
|
||||
else:
|
||||
if self.center is None:
|
||||
for row in grid_template_areas:
|
||||
del row[1]
|
||||
del grid_template_columns[1]
|
||||
|
||||
if self.left_sidebar is None:
|
||||
grid_template_areas[1][0] = grid_template_areas[1][1]
|
||||
|
||||
if self.right_sidebar is None:
|
||||
grid_template_areas[1][-1] = grid_template_areas[1][-2]
|
||||
|
||||
if (self.left_sidebar is None and
|
||||
self.right_sidebar is None and
|
||||
self.center is None):
|
||||
grid_template_areas = [['header'], ['footer']]
|
||||
grid_template_columns = ['1fr']
|
||||
grid_template_rows = ['1fr', '1fr']
|
||||
|
||||
if self.header is None:
|
||||
del grid_template_areas[0]
|
||||
del grid_template_rows[0]
|
||||
|
||||
if self.footer is None:
|
||||
del grid_template_areas[-1]
|
||||
del grid_template_rows[-1]
|
||||
|
||||
|
||||
grid_template_areas_css = "\n".join('"{}"'.format(" ".join(line))
|
||||
for line in grid_template_areas)
|
||||
|
||||
self.layout.grid_template_columns = " ".join(grid_template_columns)
|
||||
self.layout.grid_template_rows = " ".join(grid_template_rows)
|
||||
self.layout.grid_template_areas = grid_template_areas_css
|
||||
|
||||
self.children = tuple(children.values())
|
||||
|
||||
@observe("footer", "header", "center", "left_sidebar", "right_sidebar", "merge",
|
||||
"pane_widths", "pane_heights")
|
||||
def _child_changed(self, change): #pylint: disable=unused-argument
|
||||
self._update_layout()
|
||||
|
||||
|
||||
@doc_subst(_doc_snippets)
|
||||
class GridspecLayout(GridBox, LayoutProperties):
|
||||
""" Define a N by M grid layout
|
||||
|
||||
Parameters
|
||||
----------
|
||||
|
||||
n_rows : int
|
||||
number of rows in the grid
|
||||
|
||||
n_columns : int
|
||||
number of columns in the grid
|
||||
|
||||
{style_params}
|
||||
|
||||
Examples
|
||||
--------
|
||||
|
||||
>>> from ipywidgets import GridspecLayout, Button, Layout
|
||||
>>> layout = GridspecLayout(n_rows=4, n_columns=2, height='200px')
|
||||
>>> layout[:3, 0] = Button(layout=Layout(height='auto', width='auto'))
|
||||
>>> layout[1:, 1] = Button(layout=Layout(height='auto', width='auto'))
|
||||
>>> layout[-1, 0] = Button(layout=Layout(height='auto', width='auto'))
|
||||
>>> layout[0, 1] = Button(layout=Layout(height='auto', width='auto'))
|
||||
>>> layout
|
||||
"""
|
||||
|
||||
n_rows = Integer()
|
||||
n_columns = Integer()
|
||||
|
||||
def __init__(self, n_rows=None, n_columns=None, **kwargs):
|
||||
super(GridspecLayout, self).__init__(**kwargs)
|
||||
self.n_rows = n_rows
|
||||
self.n_columns = n_columns
|
||||
self._grid_template_areas = [['.'] * self.n_columns for i in range(self.n_rows)]
|
||||
|
||||
self._grid_template_rows = 'repeat(%d, 1fr)' % (self.n_rows,)
|
||||
self._grid_template_columns = 'repeat(%d, 1fr)' % (self.n_columns,)
|
||||
self._children = {}
|
||||
self._id_count = 0
|
||||
|
||||
@validate('n_rows', 'n_columns')
|
||||
def _validate_integer(self, proposal):
|
||||
if proposal['value'] > 0:
|
||||
return proposal['value']
|
||||
raise TraitError('n_rows and n_columns must be positive integer')
|
||||
|
||||
def _get_indices_from_slice(self, row, column):
|
||||
"convert a two-dimensional slice to a list of rows and column indices"
|
||||
|
||||
if isinstance(row, slice):
|
||||
start, stop, stride = row.indices(self.n_rows)
|
||||
rows = range(start, stop, stride)
|
||||
else:
|
||||
rows = [row]
|
||||
|
||||
if isinstance(column, slice):
|
||||
start, stop, stride = column.indices(self.n_columns)
|
||||
columns = range(start, stop, stride)
|
||||
else:
|
||||
columns = [column]
|
||||
|
||||
return rows, columns
|
||||
|
||||
def __setitem__(self, key, value):
|
||||
row, column = key
|
||||
self._id_count += 1
|
||||
obj_id = 'widget%03d' % self._id_count
|
||||
value.layout.grid_area = obj_id
|
||||
|
||||
rows, columns = self._get_indices_from_slice(row, column)
|
||||
|
||||
for row in rows:
|
||||
for column in columns:
|
||||
current_value = self._grid_template_areas[row][column]
|
||||
if current_value != '.' and current_value in self._children:
|
||||
del self._children[current_value]
|
||||
self._grid_template_areas[row][column] = obj_id
|
||||
|
||||
self._children[obj_id] = value
|
||||
self._update_layout()
|
||||
|
||||
def __getitem__(self, key):
|
||||
rows, columns = self._get_indices_from_slice(*key)
|
||||
|
||||
obj_id = None
|
||||
for row in rows:
|
||||
for column in columns:
|
||||
new_obj_id = self._grid_template_areas[row][column]
|
||||
obj_id = obj_id or new_obj_id
|
||||
if obj_id != new_obj_id:
|
||||
raise TypeError('The slice spans several widgets, but '
|
||||
'only a single widget can be retrieved '
|
||||
'at a time')
|
||||
|
||||
return self._children[obj_id]
|
||||
|
||||
def _update_layout(self):
|
||||
|
||||
grid_template_areas_css = "\n".join('"{}"'.format(" ".join(line))
|
||||
for line in self._grid_template_areas)
|
||||
|
||||
self.layout.grid_template_columns = self._grid_template_columns
|
||||
self.layout.grid_template_rows = self._grid_template_rows
|
||||
self.layout.grid_template_areas = grid_template_areas_css
|
||||
self.children = tuple(self._children.values())
|
||||
|
||||
|
||||
@doc_subst(_doc_snippets)
|
||||
class TwoByTwoLayout(GridBox, LayoutProperties):
|
||||
""" Define a layout with 2x2 regular grid.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
|
||||
top_left: instance of Widget
|
||||
top_right: instance of Widget
|
||||
bottom_left: instance of Widget
|
||||
bottom_right: instance of Widget
|
||||
widgets to fill the positions in the layout
|
||||
|
||||
merge: bool
|
||||
flag to say whether the empty positions should be automatically merged
|
||||
|
||||
{style_params}
|
||||
|
||||
Examples
|
||||
--------
|
||||
|
||||
>>> from ipywidgets import TwoByTwoLayout, Button
|
||||
>>> TwoByTwoLayout(top_left=Button(description="Top left"),
|
||||
... top_right=Button(description="Top right"),
|
||||
... bottom_left=Button(description="Bottom left"),
|
||||
... bottom_right=Button(description="Bottom right"))
|
||||
|
||||
"""
|
||||
|
||||
# widget positions
|
||||
top_left = Instance(Widget, allow_none=True)
|
||||
top_right = Instance(Widget, allow_none=True)
|
||||
bottom_left = Instance(Widget, allow_none=True)
|
||||
bottom_right = Instance(Widget, allow_none=True)
|
||||
|
||||
# extra args
|
||||
merge = Bool(default_value=True)
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
super(TwoByTwoLayout, self).__init__(**kwargs)
|
||||
self._update_layout()
|
||||
|
||||
def _update_layout(self):
|
||||
|
||||
|
||||
grid_template_areas = [["top-left", "top-right"],
|
||||
["bottom-left", "bottom-right"]]
|
||||
|
||||
all_children = {'top-left' : self.top_left,
|
||||
'top-right' : self.top_right,
|
||||
'bottom-left' : self.bottom_left,
|
||||
'bottom-right' : self.bottom_right}
|
||||
|
||||
children = {position : child
|
||||
for position, child in all_children.items()
|
||||
if child is not None}
|
||||
|
||||
if not children:
|
||||
return
|
||||
|
||||
for position, child in children.items():
|
||||
child.layout.grid_area = position
|
||||
|
||||
if self.merge:
|
||||
|
||||
if len(children) == 1:
|
||||
position = list(children.keys())[0]
|
||||
grid_template_areas = [[position, position],
|
||||
[position, position]]
|
||||
else:
|
||||
columns = ['left', 'right']
|
||||
for i, column in enumerate(columns):
|
||||
top, bottom = children.get('top-' + column), children.get('bottom-' + column)
|
||||
i_neighbour = (i + 1) % 2
|
||||
if top is None and bottom is None:
|
||||
# merge each cell in this column with the neighbour on the same row
|
||||
grid_template_areas[0][i] = grid_template_areas[0][i_neighbour]
|
||||
grid_template_areas[1][i] = grid_template_areas[1][i_neighbour]
|
||||
elif top is None:
|
||||
# merge with the cell below
|
||||
grid_template_areas[0][i] = grid_template_areas[1][i]
|
||||
elif bottom is None:
|
||||
# merge with the cell above
|
||||
grid_template_areas[1][i] = grid_template_areas[0][i]
|
||||
|
||||
grid_template_areas_css = "\n".join('"{}"'.format(" ".join(line))
|
||||
for line in grid_template_areas)
|
||||
|
||||
self.layout.grid_template_columns = '1fr 1fr'
|
||||
self.layout.grid_template_rows = '1fr 1fr'
|
||||
self.layout.grid_template_areas = grid_template_areas_css
|
||||
|
||||
self.children = tuple(children.values())
|
||||
|
||||
@observe("top_left", "bottom_left", "top_right", "bottom_right", "merge")
|
||||
def _child_changed(self, change): #pylint: disable=unused-argument
|
||||
self._update_layout()
|
68
venv/Lib/site-packages/ipywidgets/widgets/widget_upload.py
Normal file
68
venv/Lib/site-packages/ipywidgets/widgets/widget_upload.py
Normal file
|
@ -0,0 +1,68 @@
|
|||
# Copyright(c) Jupyter Development Team.
|
||||
# Distributed under the terms of the Modified BSD License.
|
||||
|
||||
"""FileUpload class.
|
||||
|
||||
Represents a file upload button.
|
||||
"""
|
||||
|
||||
from traitlets import (
|
||||
observe, default, Unicode, Dict, List, Int, Bool, Bytes, CaselessStrEnum
|
||||
)
|
||||
|
||||
from .widget_description import DescriptionWidget
|
||||
from .valuewidget import ValueWidget
|
||||
from .widget_core import CoreWidget
|
||||
from .widget_button import ButtonStyle
|
||||
from .widget import register, widget_serialization
|
||||
from .trait_types import bytes_serialization, InstanceDict
|
||||
|
||||
def content_from_json(value, widget):
|
||||
"""
|
||||
deserialize file content
|
||||
"""
|
||||
from_json = bytes_serialization['from_json']
|
||||
output = [from_json(e, None) for e in value]
|
||||
return output
|
||||
|
||||
|
||||
@register
|
||||
class FileUpload(DescriptionWidget, ValueWidget, CoreWidget):
|
||||
"""
|
||||
Upload file(s) from browser to Python kernel as bytes
|
||||
"""
|
||||
_model_name = Unicode('FileUploadModel').tag(sync=True)
|
||||
_view_name = Unicode('FileUploadView').tag(sync=True)
|
||||
_counter = Int().tag(sync=True)
|
||||
|
||||
accept = Unicode(help='File types to accept, empty string for all').tag(sync=True)
|
||||
multiple = Bool(help='If True, allow for multiple files upload').tag(sync=True)
|
||||
disabled = Bool(help='Enable or disable button').tag(sync=True)
|
||||
icon = Unicode('upload', help="Font-awesome icon name, without the 'fa-' prefix.").tag(sync=True)
|
||||
button_style = CaselessStrEnum(
|
||||
values=['primary', 'success', 'info', 'warning', 'danger', ''], default_value='',
|
||||
help="""Use a predefined styling for the button.""").tag(sync=True)
|
||||
style = InstanceDict(ButtonStyle).tag(sync=True, **widget_serialization)
|
||||
metadata = List(Dict(), help='List of file metadata').tag(sync=True)
|
||||
data = List(Bytes(), help='List of file content (bytes)').tag(
|
||||
sync=True, from_json=content_from_json
|
||||
)
|
||||
error = Unicode(help='Error message').tag(sync=True)
|
||||
value = Dict(read_only=True)
|
||||
|
||||
@observe('_counter')
|
||||
def on_incr_counter(self, change):
|
||||
"""
|
||||
counter increment triggers the update of trait value
|
||||
"""
|
||||
res = {}
|
||||
msg = 'Error: length of metadata and data must be equal'
|
||||
assert len(self.metadata) == len(self.data), msg
|
||||
for metadata, content in zip(self.metadata, self.data):
|
||||
name = metadata['name']
|
||||
res[name] = {'metadata': metadata, 'content': content}
|
||||
self.set_trait('value', res)
|
||||
|
||||
@default('description')
|
||||
def _default_description(self):
|
||||
return 'Upload'
|
Loading…
Add table
Add a link
Reference in a new issue