def widget_from_iterable(cls, abbrev, *args, **kwds): """ Convert an unspecified iterable to a widget. This behaves like in ipywidgets, except that an iterator (like a generator object) becomes a ``SelectionSlider``. EXAMPLES:: sage: from sage.repl.ipython_kernel.interact import sage_interactive sage: sage_interactive.widget_from_iterable([1..5]) Dropdown(options=(1, 2, 3, 4, 5), value=1) sage: sage_interactive.widget_from_iterable(iter([1..5])) SelectionSlider(options=(1, 2, 3, 4, 5), value=1) sage: sage_interactive.widget_from_iterable((1..5)) SelectionSlider(options=(1, 2, 3, 4, 5), value=1) sage: sage_interactive.widget_from_iterable(x for x in [1..5]) SelectionSlider(options=(1, 2, 3, 4, 5), value=1) sage: def gen(): ....: yield 1; yield 2; yield 3; yield 4; yield 5 sage: sage_interactive.widget_from_iterable(gen()) SelectionSlider(options=(1, 2, 3, 4, 5), value=1) """ if isinstance(abbrev, Iterator): return SelectionSlider(options=list(abbrev)) return super(sage_interactive, cls).widget_from_iterable(abbrev, *args, **kwds)
def slider(vmin, vmax=None, step_size=None, default=None, label=None, display_value=True, _range=False): """ A slider widget. INPUT: For a numeric slider (select a value from a range): - ``vmin``, ``vmax`` -- minimum and maximum value - ``step_size`` -- the step size For a selection slider (select a value from a list of values): - ``vmin`` -- a list of possible values for the slider For all sliders: - ``default`` -- initial value - ``label`` -- optional label - ``display_value`` -- (boolean) if ``True``, display the current value. EXAMPLES:: sage: from sage.repl.ipython_kernel.all_jupyter import slider sage: slider(5, label="slide me") TransformIntSlider(value=5, description=u'slide me', min=5) sage: slider(5, 20) TransformIntSlider(value=5, max=20, min=5) sage: slider(5, 20, 0.5) TransformFloatSlider(value=5.0, max=20.0, min=5.0, step=0.5) sage: slider(5, 20, default=12) TransformIntSlider(value=12, max=20, min=5) The parent of the inputs determines the parent of the value:: sage: w = slider(5); w TransformIntSlider(value=5, min=5) sage: parent(w.get_interact_value()) Integer Ring sage: w = slider(int(5)); w IntSlider(value=5, min=5) sage: parent(w.get_interact_value()) <... 'int'> sage: w = slider(5, 20, step_size=RDF("0.1")); w TransformFloatSlider(value=5.0, max=20.0, min=5.0) sage: parent(w.get_interact_value()) Real Double Field sage: w = slider(5, 20, step_size=10/3); w SelectionSlider(index=2, options=(5, 25/3, 35/3, 15, 55/3), value=35/3) sage: parent(w.get_interact_value()) Rational Field Symbolic input is evaluated numerically:: sage: w = slider(e, pi); w TransformFloatSlider(value=2.718281828459045, max=3.141592653589793, min=2.718281828459045) sage: parent(w.get_interact_value()) Real Field with 53 bits of precision For a selection slider, the default is adjusted to one of the possible values:: sage: slider(range(10), default=17/10) SelectionSlider(index=2, options=(0, 1, 2, 3, 4, 5, 6, 7, 8, 9), value=2) TESTS:: sage: slider(range(5), range(5)) Traceback (most recent call last): ... TypeError: unexpected argument 'vmax' for a selection slider sage: slider(range(5), step_size=2) Traceback (most recent call last): ... TypeError: unexpected argument 'step_size' for a selection slider sage: slider(5).readout True sage: slider(5, display_value=False).readout False """ kwds = {"readout": display_value} if label: kwds["description"] = u(label) # If vmin is iterable, return a SelectionSlider if isinstance(vmin, Iterable): if vmax is not None: raise TypeError( "unexpected argument 'vmax' for a selection slider") if step_size is not None: raise TypeError( "unexpected argument 'step_size' for a selection slider") if _range: # https://github.com/ipython/ipywidgets/issues/760 raise NotImplementedError( "range_slider does not support a list of values") options = list(vmin) # Find default in options def err(v): if v is default: return (-1, 0) try: if v == default: return (0, 0) return (0, abs(v - default)) except Exception: return (1, 0) kwds["options"] = options if default is not None: kwds["value"] = min(options, key=err) return SelectionSlider(**kwds) if default is not None: kwds["value"] = default # Sum all input numbers to figure out type/parent p = parent(sum(x for x in (vmin, vmax, step_size) if x is not None)) # Change SR to RR if p is SR: p = RR # Convert all inputs to the common parent if vmin is not None: vmin = p(vmin) if vmax is not None: vmax = p(vmax) if step_size is not None: step_size = p(step_size) def tuple_elements_p(t): "Convert all entries of the tuple `t` to `p`" return tuple(p(x) for x in t) zero = p() if isinstance(zero, Integral): if p is int: if _range: cls = IntRangeSlider else: cls = IntSlider else: if _range: kwds["transform"] = tuple_elements_p cls = TransformIntRangeSlider else: kwds["transform"] = p cls = TransformIntSlider elif isinstance(zero, Rational): # Rational => implement as SelectionSlider if _range: # https://github.com/ipython/ipywidgets/issues/760 raise NotImplementedError( "range_slider does not support rational numbers") vmin, vmax, value = _get_min_max_value(vmin, vmax, default, step_size) kwds["value"] = value kwds["options"] = srange(vmin, vmax, step_size, include_endpoint=True) return SelectionSlider(**kwds) elif isinstance(zero, Real): if p is float: if _range: cls = FloatRangeSlider else: cls = FloatSlider else: if _range: kwds["transform"] = tuple_elements_p cls = TransformFloatRangeSlider else: kwds["transform"] = p cls = TransformFloatSlider else: raise TypeError("unknown parent {!r} for slider".format(p)) kwds["min"] = vmin if vmax is not None: kwds["max"] = vmax if step_size is not None: kwds["step"] = step_size return cls(**kwds)